{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# What to expect from AutoML software\n",
    "Automated machine learning (AutoML) takes a higher-level approach to machine learning than most practitioners are used to, so we've gathered a handful of guidelines on what to expect when running AutoML software such as TPOT.\n",
    "\n",
    "#### AUTOML ALGORITHMS AREN'T INTENDED TO RUN FOR ONLY A FEW MINUTES\n",
    "Of course, you can run TPOT for only a few minutes, and it will find a reasonably good pipeline for your dataset. However, if you don't run TPOT for long enough, it may not find the best possible pipeline for your dataset. It may not even find any suitable pipeline at all, in which case a RuntimeError('A pipeline has not yet been optimized. Please call fit() first.') will be raised. Often it is worthwhile to run multiple instances of TPOT in parallel for a long time (hours to days) to allow TPOT to thoroughly search the pipeline space for your dataset.\n",
    "\n",
    "#### AUTOML ALGORITHMS CAN TAKE A LONG TIME TO FINISH THEIR SEARCH\n",
    "AutoML algorithms aren't as simple as fitting one model on the dataset; they consider multiple machine learning algorithms (random forests, linear models, SVMs, etc.) in a pipeline with multiple preprocessing steps (missing value imputation, scaling, PCA, feature selection, etc.), the hyperparameters for all of the models and preprocessing steps, and multiple ways to ensemble or stack the algorithms within the pipeline.\n",
    "\n",
    "As such, TPOT will take a while to run on larger datasets, but it's important to realize why. With the default TPOT settings (100 generations with 100 population size), TPOT will evaluate 10,000 pipeline configurations before finishing. To put this number into context, think about a grid search of 10,000 hyperparameter combinations for a machine learning algorithm and how long that grid search will take. That is 10,000 model configurations to evaluate with 10-fold cross-validation, which means that roughly 100,000 models are fit and evaluated on the training data in one grid search. That's a time-consuming procedure, even for simpler models like decision trees.\n",
    "\n",
    "Typical TPOT runs will take hours to days to finish (unless it's a small dataset), but you can always interrupt the run partway through and see the best results so far. TPOT also provides a warm_start and a periodic_checkpoint_folder parameter that lets you restart a TPOT run from where it left off.\n",
    "\n",
    "#### AUTOML ALGORITHMS CAN RECOMMEND DIFFERENT SOLUTIONS FOR THE SAME DATASET\n",
    "If you're working with a reasonably complex dataset or run TPOT for a short amount of time, different TPOT runs may result in different pipeline recommendations. TPOT's optimization algorithm is stochastic, which means that it uses randomness (in part) to search the possible pipeline space. When two TPOT runs recommend different pipelines, this means that the TPOT runs didn't converge due to lack of time or that multiple pipelines perform more-or-less the same on your dataset.\n",
    "\n",
    "This is actually an advantage over fixed grid search techniques: TPOT is meant to be an assistant that gives you ideas on how to solve a particular machine learning problem by exploring pipeline configurations that you might have never considered, then leaves the fine-tuning to more constrained parameter tuning techniques such as grid search or bayesian optimization."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# TPOT with code\n",
    "\n",
    "We've designed the TPOT interface to be as similar as possible to scikit-learn.\n",
    "\n",
    "TPOT can be imported just like any regular Python module. To import TPOT, type:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Matplotlib is building the font cache; this may take a moment.\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
      "  from .autonotebook import tqdm as notebook_tqdm\n"
     ]
    }
   ],
   "source": [
    "import tpot\n",
    "from tpot import TPOTClassifier"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "then create an instance of TPOT as follows:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "classification_optimizer = TPOTClassifier()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It's also possible to use TPOT for regression problems with the TPOTRegressor class. Other than the class name, a TPOTRegressor is used the same way as a TPOTClassifier. You can read more about the TPOTClassifier and TPOTRegressor classes in the API documentation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "from tpot import TPOTRegressor\n",
    "regression_optimizer = TPOTRegressor()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Fitting a TPOT model works exactly like any other sklearn estimator. Some example code with custom TPOT parameters might look like:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Generation: : 5it [00:32,  6.57s/it]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "auroc_score:  0.9950396825396826\n"
     ]
    }
   ],
   "source": [
    "import sklearn\n",
    "import sklearn.datasets\n",
    "import sklearn.metrics\n",
    "import tpot\n",
    "\n",
    "classification_optimizer = TPOTClassifier(search_space=\"linear-light\", max_time_mins=30/60, n_jobs=30, cv=5)\n",
    "\n",
    "X, y = sklearn.datasets.load_breast_cancer(return_X_y=True)\n",
    "X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, random_state=1, test_size=0.2)\n",
    "\n",
    "classification_optimizer.fit(X_train, y_train)\n",
    "\n",
    "auroc_score = sklearn.metrics.roc_auc_score(y_test, classification_optimizer.predict_proba(X_test)[:,1])\n",
    "print(\"auroc_score: \", auroc_score)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Scorers, Objective Functions, and multi objective optimization.\n",
    "\n",
    "There are two ways of passing objectives into TPOT. \n",
    "\n",
    "1. `scorers`: Scorers are functions that have the signature (estimator, X_test, y_test) and take in estimators that are expected to be fitted to training data. These can be produced with the [sklearn.metrics.make_scorer](https://scikit-learn.org/stable/modules/generated/sklearn.metrics.make_scorer.html) function. This function is used to evaluate the test folds during cross validation (defined in the `cv` parameter). These are passed into TPOT via the scorers parameter. This can take in the scorer itself or the string corresponding to a scoring function ([as listed here](https://scikit-learn.org/stable/modules/model_evaluation.html)). TPOT also supports passing in a list of several scorers for multi-objective optimization. For each fold of CV, TPOT only fits the estimator once, then evaluates all provided scorers in a loop.\n",
    "\n",
    "2. `other_objective_functions` : Other objective functions in TPOT have the signature (estimator) and returns a float or list of floats. These get passed a single unfitted estimator once, outside of cross validation. The user may choose to fit the pipeline within this objective function as well.\n",
    "\n",
    "\n",
    "\n",
    "Each scorer and objective function must be accompanied by a list of weights corresponding to the list of objectives, these are `scorers_weights` and `other_objective_function_weights`, respectively. By default, TPOT maximizes objective functions (this can be changed by `bigger_is_better=False`). Positive weights means that TPOT will seek to maximize that objective, and negative weights correspond to minimization. For most selectors (and the default), only the sign matters. The scale of the weight may matter if using a custom selection function for the optimization algorithm. A zero weight means that the score will not have an impact on the selection algorithm.\n",
    "\n",
    "Here is an example of using two scorers\n",
    "\n",
    "    scorers=['roc_auc_ovr',tpot.objectives.complexity_scorer],\n",
    "    scorers_weights=[1,-1],\n",
    "\n",
    "\n",
    "Here is an example with a scorer and a secondary objective function\n",
    "\n",
    "    scorers=['roc_auc_ovr'],\n",
    "    scorers_weights=[1],\n",
    "    other_objective_functions=[tpot.objectives.number_of_leaves_objective],\n",
    "    other_objective_functions_weights=[-1],\n",
    "\n",
    "\n",
    "TPOT will always automatically name the scorers based on the function name for the columns in the final results dataframe. TPOT will use the function name as the column name for `other_objective_functions`. However, if you would like to specify custom column names, you can set the `objective_function_names` to be a list of names (str) for each value returned by the function in `other_objective_functions`. This can be useful if your additional functions return more than one value per function.\n",
    "\n",
    "It is possible to have either the scorer or other_objective_function to return multiple values. In that case, just make sure that the `scorers_weights` and `other_objective_function_weights` are the same length as the number of returned scores.\n",
    "\n",
    "\n",
    "TPOT comes with a few additional built in objective functions you can use. The first table are objectives applied to fitted pipelines, and thus are passee into the `scorers` parameter. The second table are objective functions for the `other_objective_functions` param.\n",
    "\n",
    "Scorers:\n",
    "| Function     | Description      |\n",
    "| :---        |    :----:   |\n",
    "| tpot.objectives.complexity_scorer | Estimates the number of learned parameters across all classifiers and regressors in the pipelines. Additionally, currently transformers add 1 point and selectors add 0 points (since they don't affect the complexity of the \"final\" predictive pipeline.) |\n",
    "\n",
    "Other Objective Functions.\n",
    "\n",
    "| Function     | Description      |\n",
    "| :---        |    :----:   |\n",
    "| tpot.objectives.average_path_length | Computes the average shortest path from all nodes to the root/final estimator (only supported for GraphPipeline) |\n",
    "| tpot.objectives.number_of_leaves_objective | Calculates the number of leaves (input nodes) in a GraphPipeline |\n",
    "| tpot.objectives.number_of_nodes_objective | Calculates the number of nodes in a pipeline (whether it is an scikit-learn Pipeline, GraphPipeline, Feature Union, or the previous nested within each other) |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Measuring Model Complexity\n",
    "\n",
    "When running TPOT, including a secondary objective that measures model complexity can sometimes be beneficial. More complex models can yield higher performance, but this comes at the cost of interpretability. Simpler models may be more interpretable but often have lower predictive performance. Sometimes, however, vast increases in complexity only marginally improve predictive performance. There may be other simpler and more interpretable pipelines with marginal performance decreases that could be acceptable for the increased interpretability. However, these pipelines are often missed when optimizing purely for performance. By including both performance and complexity as objective functions, TPOT will attempt to optimize the best pipeline for all complexity levels simultaneously. After optimization, the user will be able to see the complexity vs performance tradeoff and decide which pipeline best suits their needs. \n",
    "\n",
    "Two methods of measuring complexity to consider would be `tpot.objectives.number_of_nodes_objective` or `tpot.objectives.complexity_scorer`. The number of nodes objective simply calculates the number of steps within a pipeline. This is a simple metric, however it does not differentiate between the complexity of different model types. For example, a simple LogisticRegression counts the same as the much more complex XGBoost. The complexity scorer tries to estimate the number of learned parameters included in the classifiers and regressors of the pipeline. It is challenging and potentially subjective how to exactly quantify and compare complexity between different classes of models. However, this function provides a reasonable heuristic for the evolutionary algorithm that at least separates out qualitatively more or less complex algorithms from one another. While it may be hard to compare the relative complexities of LogisticRegression and XGBoost exactly, for example, both will always be on opposite ends of the complexity values returned by this function. This allows for pareto fronts with LogisticRegression on one side, and XGBoost on the other.\n",
    "\n",
    "An example of this analysis is demonstrated in a following section."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Built In Configurations\n",
    "TPOT can be used to optimize hyperparameters, select models, and optimize pipelines of models including determining the sequence of steps. **Tutorial 2** goes into more detail on how to customize search spaces with custom hyperparameter ranges, model types, and possible pipeline configurations. TPOT also comes with a handful of default operators and parameter configurations that we believe work well for optimizing machine learning pipelines. Below is a list of the current built-in configurations that come with TPOT. These can be passed in as strings to the `search space` parameter of any of the TPOT estimators.\n",
    "\n",
    "| String     | Description      |\n",
    "| :---        |    :----:   |\n",
    "| linear  | A linear pipeline with the structure of \"Selector->(transformers+Passthrough)->(classifiers/regressors+Passthrough)->final classifier/regressor.\" For both the transformer and inner estimator layers, TPOT may choose one or more transformers/classifiers, or it may choose none. The inner classifier/regressor layer is optional. |\n",
    "| linear-light | Same search space as linear, but without the inner classifier/regressor layer and with a reduced set of faster running estimators. |\n",
    "| graph | TPOT will optimize a pipeline in the shape of a directed acyclic graph. The nodes of the graph can include selectors, scalers, transformers, or classifiers/regressors (inner classifiers/regressors can optionally be not included). This will return a custom GraphPipeline rather than an sklearn Pipeline. More details in Tutorial 6. |\n",
    "| graph-light | Same as graph search space, but without the inner classifier/regressors and with a reduced set of faster running estimators. |\n",
    "| mdr |TPOT will search over a series of feature selectors and Multifactor Dimensionality Reduction models to find a series of operators that maximize prediction accuracy. The TPOT MDR configuration is specialized for genome-wide association studies (GWAS), and is described in detail online here.\n",
    "\n",
    "Note that TPOT MDR may be slow to run because the feature selection routines are computationally expensive, especially on large datasets. |\n",
    "\n",
    "The `linear` and `graph` configurations by default allow for additional stacked classifiers/regressors within the pipeline in addition to the final classifier/regressor. If you would like to disable this, you can manually get the search space without inner classifier/regressors through the function `tpot.config.template_search_spaces.get_template_search_spaces` with `inner_predictios=False`. You can pass the resulting search space into the `search space` param. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "import tpot\n",
    "from tpot.search_spaces.pipelines import SequentialPipeline\n",
    "from tpot.config import get_search_space\n",
    "\n",
    "stc_search_space = SequentialPipeline([\n",
    "    get_search_space(\"selectors\"),\n",
    "    get_search_space(\"all_transformers\"),\n",
    "    get_search_space(\"classifiers\"),\n",
    "])\n",
    "\n",
    "est = tpot.TPOTEstimator(\n",
    "    search_space = stc_search_space,\n",
    "    scorers=[\"roc_auc_ovr\", tpot.objectives.complexity_scorer],\n",
    "    scorers_weights=[1.0, -1.0],\n",
    "    classification = True,\n",
    "    cv = 5,\n",
    "    max_eval_time_mins = 10,\n",
    "    early_stop = 2,\n",
    "    verbose = 2,\n",
    "    n_jobs=4,\n",
    ")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Using a built in method"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "est = tpot.TPOTEstimator(\n",
    "    search_space = \"linear\",\n",
    "    scorers=[\"roc_auc_ovr\", tpot.objectives.complexity_scorer],\n",
    "    scorers_weights=[1.0, -1.0],\n",
    "    classification = True,\n",
    "    cv = 5,\n",
    "    max_eval_time_mins = 10,\n",
    "    early_stop = 2,\n",
    "    verbose = 2,\n",
    "    n_jobs=4,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The specific hyperparameter ranges used by TPOT can be found in files in the tpot/config folder. The template search spaces listed above are defined in tpot/config/template_search_spaces.py. Search spaces for individual models can be acquired in the tpot/config/get_configspace.py file (`tpot.config.get_search_space`). More details on customizing search spaces can be found in Tutorial 2.\n",
    "\n",
    "\n",
    "    `tpot.config.template_search_spaces.get_template_search_spaces`\n",
    "    Returns a search space which can be optimized by TPOT.\n",
    "\n",
    "    Parameters\n",
    "    ----------\n",
    "    search_space: str or SearchSpace\n",
    "        The default search space to use. If a string, it should be one of the following:\n",
    "            - 'linear': A search space for linear pipelines\n",
    "            - 'linear-light': A search space for linear pipelines with a smaller, faster search space\n",
    "            - 'graph': A search space for graph pipelines\n",
    "            - 'graph-light': A search space for graph pipelines with a smaller, faster search space\n",
    "            - 'mdr': A search space for MDR pipelines\n",
    "        If a SearchSpace object, it should be a valid search space object for TPOT.\n",
    "    \n",
    "    classification: bool, default=True\n",
    "        Whether the problem is a classification problem or a regression problem.\n",
    "\n",
    "    inner_predictors: bool, default=None\n",
    "        Whether to include additional classifiers/regressors before the final classifier/regressor (allowing for ensembles). \n",
    "        Defaults to False for 'linear-light' and 'graph-light' search spaces, and True otherwise. (Not used for 'mdr' search space)\n",
    "    \n",
    "    cross_val_predict_cv: int, default=None\n",
    "        The number of folds to use for cross_val_predict. \n",
    "        Defaults to 0 for 'linear-light' and 'graph-light' search spaces, and 5 otherwise. (Not used for 'mdr' search space)\n",
    "\n",
    "    get_search_space_params: dict\n",
    "        Additional parameters to pass to the get_search_space function.\n",
    "\n",
    "### cross_val_predict_cv\n",
    "\n",
    "Additionally, utilizing `cross_val_predict_cv` may increase performance when training models with inner classifiers/regressors. If this parameter is set, during model training any classifiers or regressors that is not the final predictor will use `sklearn.model_selection.cross_val_predict` to pass out of sample predictions into the following steps of the model. The model will still be fit to the full data which will be used for predictions after training. Training downstream models on out of sample predictions can often prevent overfitting and increase performance. The reason is that this gives downstream models a estimate of how upstream models compare on unseen data. Otherwise, if an upsteam model heavily overfits the data, downsteam models may simply learn to blindly trust the seemingly well-predicting model, propagating the over-fitting through to the end result.\n",
    "\n",
    "The downside is that cross_val_predict_cv is significantly more computationally demanding, and may not be necessary for your given dataset. \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "linear_with_cross_val_predict_sp = tpot.config.template_search_spaces.get_template_search_spaces(search_space=\"linear\", classification=True, inner_predictors=True, cross_val_predict_cv=5)\n",
    "classification_optimizer = TPOTClassifier(search_space=linear_with_cross_val_predict_sp, max_time_mins=30/60, n_jobs=30, cv=5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Terminating Optimization (Early Stopping)\n",
    "\n",
    "Note that we use a short time duration for a quick example, but in practice, you may need to run TPOT for a longer duration. By default, TPOT sets a time limit of 1 hour with a max limit of 5 minutes per pipeline. In practice, you may want to increase these values.\n",
    "\n",
    "There are three methods of terminating a TPOT run and ending the optimization process. TPOT will terminate as soon as one of the conditions is met.\n",
    "* `max_time_mins` : (Default, 60 minutes) After this many minutes, TPOT will terminate and return the best pipeline it found so far.\n",
    "* `early_stop` : The number of generations without seeing an improvement in performance, after which TPOT terminates. Generally, a value of around 5 to 20 is sufficient to be reasonably sure that performance has converged.\n",
    "* `generations`: The total number of generations of the evolutionary algorithm to run.\n",
    "\n",
    "By default, TPOT will run until the time limit is up, with no generation or early stop limits."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Best Practices and tips:\n",
    "\n",
    "* When running tpot from an .py script, it is important to protect code with `if __name__==\"__main__\":` . This is because of how TPOT handles parallelization with Python and Dask."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Generation: : 1it [03:13, 193.20s/it]\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 0 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 1 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 2 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 3 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 4 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 5 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 6 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 7 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 8 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 9 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 10 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 11 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 12 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 13 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 14 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 15 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 16 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 17 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 18 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 19 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 20 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 21 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 22 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 23 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 24 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 25 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 26 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 27 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 28 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 29 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 30 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 31 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 32 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 33 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 34 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 35 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 36 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 37 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 38 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 39 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 40 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 41 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 42 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 43 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 44 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 45 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 46 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 47 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 48 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 49 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 50 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 51 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 52 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 53 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 54 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 55 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 56 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 57 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 58 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_discretization.py:307: UserWarning: Bins whose width are too small (i.e., <= 1e-8) in feature 59 are removed. Consider decreasing the number of bins.\n",
      "  warnings.warn(\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.999621947852182\n"
     ]
    }
   ],
   "source": [
    "from dask.distributed import Client, LocalCluster\n",
    "import tpot\n",
    "import sklearn\n",
    "import sklearn.datasets\n",
    "import numpy as np\n",
    "\n",
    "if __name__==\"__main__\":\n",
    "    scorer = sklearn.metrics.get_scorer('roc_auc_ovo')\n",
    "    X, y = sklearn.datasets.load_digits(return_X_y=True)\n",
    "    X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, train_size=0.75, test_size=0.25)\n",
    "\n",
    "\n",
    "    est = tpot.TPOTClassifier(n_jobs=4, max_time_mins=3, verbose=2, early_stop=3)\n",
    "    est.fit(X_train, y_train)\n",
    "\n",
    "\n",
    "    print(scorer(est, X_test, y_test))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Example analysis and the Estimator class \n",
    "\n",
    "Here we use a toy example dataset included in scikit-learn. We will use the `light` configuration and the `complexity_scorer` to estimate complexity.\n",
    "\n",
    "Note, for this toy example, we set a relatively short run time. In practice, we would recommend running TPOT for a longer duration with an `early_stop` value of around 5 to 20 (more details below)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Generation: : 4it [02:34, 38.64s/it]\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/neural_network/_multilayer_perceptron.py:690: ConvergenceWarning: Stochastic Optimizer: Maximum iterations (200) reached and the optimization hasn't converged yet.\n",
      "  warnings.warn(\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.9978289188015632\n"
     ]
    }
   ],
   "source": [
    "from dask.distributed import Client, LocalCluster\n",
    "import tpot\n",
    "import sklearn\n",
    "import sklearn.datasets\n",
    "import numpy as np\n",
    "\n",
    "import tpot.objectives\n",
    "\n",
    "\n",
    "scorer = sklearn.metrics.get_scorer('roc_auc_ovr')\n",
    "\n",
    "X, y = sklearn.datasets.load_breast_cancer(return_X_y=True)\n",
    "X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, train_size=0.75, test_size=0.25)\n",
    "\n",
    "\n",
    "est = tpot.TPOTClassifier(\n",
    "    scorers=[scorer, tpot.objectives.complexity_scorer],\n",
    "    scorers_weights=[1.0, -1.0],\n",
    "\n",
    "    search_space=\"linear\",\n",
    "    n_jobs=4, \n",
    "    max_time_mins=60, \n",
    "    max_eval_time_mins=10,\n",
    "    early_stop=2,\n",
    "    verbose=2,)\n",
    "est.fit(X_train, y_train)\n",
    "\n",
    "print(scorer(est, X_test, y_test))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can access the best pipeline selected by TPOT with the `fitted_pipeline_` attribute. This is the pipeline with the highest cross validation score (on the first scorer, or first objective function if no scorer is provided.)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>#sk-container-id-1 {\n",
       "  /* Definition of color scheme common for light and dark mode */\n",
       "  --sklearn-color-text: black;\n",
       "  --sklearn-color-line: gray;\n",
       "  /* Definition of color scheme for unfitted estimators */\n",
       "  --sklearn-color-unfitted-level-0: #fff5e6;\n",
       "  --sklearn-color-unfitted-level-1: #f6e4d2;\n",
       "  --sklearn-color-unfitted-level-2: #ffe0b3;\n",
       "  --sklearn-color-unfitted-level-3: chocolate;\n",
       "  /* Definition of color scheme for fitted estimators */\n",
       "  --sklearn-color-fitted-level-0: #f0f8ff;\n",
       "  --sklearn-color-fitted-level-1: #d4ebff;\n",
       "  --sklearn-color-fitted-level-2: #b3dbfd;\n",
       "  --sklearn-color-fitted-level-3: cornflowerblue;\n",
       "\n",
       "  /* Specific color for light theme */\n",
       "  --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
       "  --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, white)));\n",
       "  --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
       "  --sklearn-color-icon: #696969;\n",
       "\n",
       "  @media (prefers-color-scheme: dark) {\n",
       "    /* Redefinition of color scheme for dark theme */\n",
       "    --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
       "    --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, #111)));\n",
       "    --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
       "    --sklearn-color-icon: #878787;\n",
       "  }\n",
       "}\n",
       "\n",
       "#sk-container-id-1 {\n",
       "  color: var(--sklearn-color-text);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 pre {\n",
       "  padding: 0;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 input.sk-hidden--visually {\n",
       "  border: 0;\n",
       "  clip: rect(1px 1px 1px 1px);\n",
       "  clip: rect(1px, 1px, 1px, 1px);\n",
       "  height: 1px;\n",
       "  margin: -1px;\n",
       "  overflow: hidden;\n",
       "  padding: 0;\n",
       "  position: absolute;\n",
       "  width: 1px;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-dashed-wrapped {\n",
       "  border: 1px dashed var(--sklearn-color-line);\n",
       "  margin: 0 0.4em 0.5em 0.4em;\n",
       "  box-sizing: border-box;\n",
       "  padding-bottom: 0.4em;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-container {\n",
       "  /* jupyter's `normalize.less` sets `[hidden] { display: none; }`\n",
       "     but bootstrap.min.css set `[hidden] { display: none !important; }`\n",
       "     so we also need the `!important` here to be able to override the\n",
       "     default hidden behavior on the sphinx rendered scikit-learn.org.\n",
       "     See: https://github.com/scikit-learn/scikit-learn/issues/21755 */\n",
       "  display: inline-block !important;\n",
       "  position: relative;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-text-repr-fallback {\n",
       "  display: none;\n",
       "}\n",
       "\n",
       "div.sk-parallel-item,\n",
       "div.sk-serial,\n",
       "div.sk-item {\n",
       "  /* draw centered vertical line to link estimators */\n",
       "  background-image: linear-gradient(var(--sklearn-color-text-on-default-background), var(--sklearn-color-text-on-default-background));\n",
       "  background-size: 2px 100%;\n",
       "  background-repeat: no-repeat;\n",
       "  background-position: center center;\n",
       "}\n",
       "\n",
       "/* Parallel-specific style estimator block */\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item::after {\n",
       "  content: \"\";\n",
       "  width: 100%;\n",
       "  border-bottom: 2px solid var(--sklearn-color-text-on-default-background);\n",
       "  flex-grow: 1;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel {\n",
       "  display: flex;\n",
       "  align-items: stretch;\n",
       "  justify-content: center;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  position: relative;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item {\n",
       "  display: flex;\n",
       "  flex-direction: column;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item:first-child::after {\n",
       "  align-self: flex-end;\n",
       "  width: 50%;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item:last-child::after {\n",
       "  align-self: flex-start;\n",
       "  width: 50%;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item:only-child::after {\n",
       "  width: 0;\n",
       "}\n",
       "\n",
       "/* Serial-specific style estimator block */\n",
       "\n",
       "#sk-container-id-1 div.sk-serial {\n",
       "  display: flex;\n",
       "  flex-direction: column;\n",
       "  align-items: center;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  padding-right: 1em;\n",
       "  padding-left: 1em;\n",
       "}\n",
       "\n",
       "\n",
       "/* Toggleable style: style used for estimator/Pipeline/ColumnTransformer box that is\n",
       "clickable and can be expanded/collapsed.\n",
       "- Pipeline and ColumnTransformer use this feature and define the default style\n",
       "- Estimators will overwrite some part of the style using the `sk-estimator` class\n",
       "*/\n",
       "\n",
       "/* Pipeline and ColumnTransformer style (default) */\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable {\n",
       "  /* Default theme specific background. It is overwritten whether we have a\n",
       "  specific estimator or a Pipeline/ColumnTransformer */\n",
       "  background-color: var(--sklearn-color-background);\n",
       "}\n",
       "\n",
       "/* Toggleable label */\n",
       "#sk-container-id-1 label.sk-toggleable__label {\n",
       "  cursor: pointer;\n",
       "  display: block;\n",
       "  width: 100%;\n",
       "  margin-bottom: 0;\n",
       "  padding: 0.5em;\n",
       "  box-sizing: border-box;\n",
       "  text-align: center;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 label.sk-toggleable__label-arrow:before {\n",
       "  /* Arrow on the left of the label */\n",
       "  content: \"▸\";\n",
       "  float: left;\n",
       "  margin-right: 0.25em;\n",
       "  color: var(--sklearn-color-icon);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 label.sk-toggleable__label-arrow:hover:before {\n",
       "  color: var(--sklearn-color-text);\n",
       "}\n",
       "\n",
       "/* Toggleable content - dropdown */\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content {\n",
       "  max-height: 0;\n",
       "  max-width: 0;\n",
       "  overflow: hidden;\n",
       "  text-align: left;\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content.fitted {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content pre {\n",
       "  margin: 0.2em;\n",
       "  border-radius: 0.25em;\n",
       "  color: var(--sklearn-color-text);\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content.fitted pre {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 input.sk-toggleable__control:checked~div.sk-toggleable__content {\n",
       "  /* Expand drop-down */\n",
       "  max-height: 200px;\n",
       "  max-width: 100%;\n",
       "  overflow: auto;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 input.sk-toggleable__control:checked~label.sk-toggleable__label-arrow:before {\n",
       "  content: \"▾\";\n",
       "}\n",
       "\n",
       "/* Pipeline/ColumnTransformer-specific style */\n",
       "\n",
       "#sk-container-id-1 div.sk-label input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-label.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Estimator-specific style */\n",
       "\n",
       "/* Colorize estimator box */\n",
       "#sk-container-id-1 div.sk-estimator input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-estimator.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-label label.sk-toggleable__label,\n",
       "#sk-container-id-1 div.sk-label label {\n",
       "  /* The background is the default theme color */\n",
       "  color: var(--sklearn-color-text-on-default-background);\n",
       "}\n",
       "\n",
       "/* On hover, darken the color of the background */\n",
       "#sk-container-id-1 div.sk-label:hover label.sk-toggleable__label {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "/* Label box, darken color on hover, fitted */\n",
       "#sk-container-id-1 div.sk-label.fitted:hover label.sk-toggleable__label.fitted {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Estimator label */\n",
       "\n",
       "#sk-container-id-1 div.sk-label label {\n",
       "  font-family: monospace;\n",
       "  font-weight: bold;\n",
       "  display: inline-block;\n",
       "  line-height: 1.2em;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-label-container {\n",
       "  text-align: center;\n",
       "}\n",
       "\n",
       "/* Estimator-specific */\n",
       "#sk-container-id-1 div.sk-estimator {\n",
       "  font-family: monospace;\n",
       "  border: 1px dotted var(--sklearn-color-border-box);\n",
       "  border-radius: 0.25em;\n",
       "  box-sizing: border-box;\n",
       "  margin-bottom: 0.5em;\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-estimator.fitted {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "/* on hover */\n",
       "#sk-container-id-1 div.sk-estimator:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-estimator.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Specification for estimator info (e.g. \"i\" and \"?\") */\n",
       "\n",
       "/* Common style for \"i\" and \"?\" */\n",
       "\n",
       ".sk-estimator-doc-link,\n",
       "a:link.sk-estimator-doc-link,\n",
       "a:visited.sk-estimator-doc-link {\n",
       "  float: right;\n",
       "  font-size: smaller;\n",
       "  line-height: 1em;\n",
       "  font-family: monospace;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  border-radius: 1em;\n",
       "  height: 1em;\n",
       "  width: 1em;\n",
       "  text-decoration: none !important;\n",
       "  margin-left: 1ex;\n",
       "  /* unfitted */\n",
       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-unfitted-level-1);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link.fitted,\n",
       "a:link.sk-estimator-doc-link.fitted,\n",
       "a:visited.sk-estimator-doc-link.fitted {\n",
       "  /* fitted */\n",
       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-fitted-level-1);\n",
       "}\n",
       "\n",
       "/* On hover */\n",
       "div.sk-estimator:hover .sk-estimator-doc-link:hover,\n",
       ".sk-estimator-doc-link:hover,\n",
       "div.sk-label-container:hover .sk-estimator-doc-link:hover,\n",
       ".sk-estimator-doc-link:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "div.sk-estimator.fitted:hover .sk-estimator-doc-link.fitted:hover,\n",
       ".sk-estimator-doc-link.fitted:hover,\n",
       "div.sk-label-container:hover .sk-estimator-doc-link.fitted:hover,\n",
       ".sk-estimator-doc-link.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "/* Span, style for the box shown on hovering the info icon */\n",
       ".sk-estimator-doc-link span {\n",
       "  display: none;\n",
       "  z-index: 9999;\n",
       "  position: relative;\n",
       "  font-weight: normal;\n",
       "  right: .2ex;\n",
       "  padding: .5ex;\n",
       "  margin: .5ex;\n",
       "  width: min-content;\n",
       "  min-width: 20ex;\n",
       "  max-width: 50ex;\n",
       "  color: var(--sklearn-color-text);\n",
       "  box-shadow: 2pt 2pt 4pt #999;\n",
       "  /* unfitted */\n",
       "  background: var(--sklearn-color-unfitted-level-0);\n",
       "  border: .5pt solid var(--sklearn-color-unfitted-level-3);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link.fitted span {\n",
       "  /* fitted */\n",
       "  background: var(--sklearn-color-fitted-level-0);\n",
       "  border: var(--sklearn-color-fitted-level-3);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link:hover span {\n",
       "  display: block;\n",
       "}\n",
       "\n",
       "/* \"?\"-specific style due to the `<a>` HTML tag */\n",
       "\n",
       "#sk-container-id-1 a.estimator_doc_link {\n",
       "  float: right;\n",
       "  font-size: 1rem;\n",
       "  line-height: 1em;\n",
       "  font-family: monospace;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  border-radius: 1rem;\n",
       "  height: 1rem;\n",
       "  width: 1rem;\n",
       "  text-decoration: none;\n",
       "  /* unfitted */\n",
       "  color: var(--sklearn-color-unfitted-level-1);\n",
       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 a.estimator_doc_link.fitted {\n",
       "  /* fitted */\n",
       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-fitted-level-1);\n",
       "}\n",
       "\n",
       "/* On hover */\n",
       "#sk-container-id-1 a.estimator_doc_link:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 a.estimator_doc_link.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-3);\n",
       "}\n",
       "</style><div id=\"sk-container-id-1\" class=\"sk-top-container\"><div class=\"sk-text-repr-fallback\"><pre>Pipeline(steps=[(&#x27;minmaxscaler&#x27;, MinMaxScaler()),\n",
       "                (&#x27;selectpercentile&#x27;,\n",
       "                 SelectPercentile(percentile=68.60012151662)),\n",
       "                (&#x27;featureunion-1&#x27;,\n",
       "                 FeatureUnion(transformer_list=[(&#x27;skiptransformer&#x27;,\n",
       "                                                 SkipTransformer()),\n",
       "                                                (&#x27;passthrough&#x27;,\n",
       "                                                 Passthrough())])),\n",
       "                (&#x27;featureunion-2&#x27;,\n",
       "                 FeatureUnion(transformer_list=[(&#x27;skiptransformer&#x27;,\n",
       "                                                 SkipTransformer()),\n",
       "                                                (&#x27;passthrough&#x27;,\n",
       "                                                 Passthrough())])),\n",
       "                (&#x27;mlpclassifier&#x27;,\n",
       "                 MLPClassifier(activation=&#x27;identity&#x27;, alpha=0.0023692590029,\n",
       "                               hidden_layer_sizes=[139, 139],\n",
       "                               learning_rate=&#x27;invscaling&#x27;,\n",
       "                               learning_rate_init=0.0004707733364,\n",
       "                               n_iter_no_change=32))])</pre><b>In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. <br />On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.</b></div><div class=\"sk-container\" hidden><div class=\"sk-item sk-dashed-wrapped\"><div class=\"sk-label-container\"><div class=\"sk-label fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-1\" type=\"checkbox\" ><label for=\"sk-estimator-id-1\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">&nbsp;&nbsp;Pipeline<a class=\"sk-estimator-doc-link fitted\" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.5/modules/generated/sklearn.pipeline.Pipeline.html\">?<span>Documentation for Pipeline</span></a><span class=\"sk-estimator-doc-link fitted\">i<span>Fitted</span></span></label><div class=\"sk-toggleable__content fitted\"><pre>Pipeline(steps=[(&#x27;minmaxscaler&#x27;, MinMaxScaler()),\n",
       "                (&#x27;selectpercentile&#x27;,\n",
       "                 SelectPercentile(percentile=68.60012151662)),\n",
       "                (&#x27;featureunion-1&#x27;,\n",
       "                 FeatureUnion(transformer_list=[(&#x27;skiptransformer&#x27;,\n",
       "                                                 SkipTransformer()),\n",
       "                                                (&#x27;passthrough&#x27;,\n",
       "                                                 Passthrough())])),\n",
       "                (&#x27;featureunion-2&#x27;,\n",
       "                 FeatureUnion(transformer_list=[(&#x27;skiptransformer&#x27;,\n",
       "                                                 SkipTransformer()),\n",
       "                                                (&#x27;passthrough&#x27;,\n",
       "                                                 Passthrough())])),\n",
       "                (&#x27;mlpclassifier&#x27;,\n",
       "                 MLPClassifier(activation=&#x27;identity&#x27;, alpha=0.0023692590029,\n",
       "                               hidden_layer_sizes=[139, 139],\n",
       "                               learning_rate=&#x27;invscaling&#x27;,\n",
       "                               learning_rate_init=0.0004707733364,\n",
       "                               n_iter_no_change=32))])</pre></div> </div></div><div class=\"sk-serial\"><div class=\"sk-item\"><div class=\"sk-estimator fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-2\" type=\"checkbox\" ><label for=\"sk-estimator-id-2\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">&nbsp;MinMaxScaler<a class=\"sk-estimator-doc-link fitted\" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.5/modules/generated/sklearn.preprocessing.MinMaxScaler.html\">?<span>Documentation for MinMaxScaler</span></a></label><div class=\"sk-toggleable__content fitted\"><pre>MinMaxScaler()</pre></div> </div></div><div class=\"sk-item\"><div class=\"sk-estimator fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-3\" type=\"checkbox\" ><label for=\"sk-estimator-id-3\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">&nbsp;SelectPercentile<a class=\"sk-estimator-doc-link fitted\" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.5/modules/generated/sklearn.feature_selection.SelectPercentile.html\">?<span>Documentation for SelectPercentile</span></a></label><div class=\"sk-toggleable__content fitted\"><pre>SelectPercentile(percentile=68.60012151662)</pre></div> </div></div><div class=\"sk-item sk-dashed-wrapped\"><div class=\"sk-label-container\"><div class=\"sk-label fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-4\" type=\"checkbox\" ><label for=\"sk-estimator-id-4\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">&nbsp;featureunion-1: FeatureUnion<a class=\"sk-estimator-doc-link fitted\" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.5/modules/generated/sklearn.pipeline.FeatureUnion.html\">?<span>Documentation for featureunion-1: FeatureUnion</span></a></label><div class=\"sk-toggleable__content fitted\"><pre>FeatureUnion(transformer_list=[(&#x27;skiptransformer&#x27;, SkipTransformer()),\n",
       "                               (&#x27;passthrough&#x27;, Passthrough())])</pre></div> </div></div><div class=\"sk-parallel\"><div class=\"sk-parallel-item\"><div class=\"sk-item\"><div class=\"sk-label-container\"><div class=\"sk-label fitted sk-toggleable\"><label>skiptransformer</label></div></div><div class=\"sk-serial\"><div class=\"sk-item\"><div class=\"sk-estimator fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-5\" type=\"checkbox\" ><label for=\"sk-estimator-id-5\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">SkipTransformer</label><div class=\"sk-toggleable__content fitted\"><pre>SkipTransformer()</pre></div> </div></div></div></div></div><div class=\"sk-parallel-item\"><div class=\"sk-item\"><div class=\"sk-label-container\"><div class=\"sk-label fitted sk-toggleable\"><label>passthrough</label></div></div><div class=\"sk-serial\"><div class=\"sk-item\"><div class=\"sk-estimator fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-6\" type=\"checkbox\" ><label for=\"sk-estimator-id-6\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">Passthrough</label><div class=\"sk-toggleable__content fitted\"><pre>Passthrough()</pre></div> </div></div></div></div></div></div></div><div class=\"sk-item sk-dashed-wrapped\"><div class=\"sk-label-container\"><div class=\"sk-label fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-7\" type=\"checkbox\" ><label for=\"sk-estimator-id-7\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">&nbsp;featureunion-2: FeatureUnion<a class=\"sk-estimator-doc-link fitted\" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.5/modules/generated/sklearn.pipeline.FeatureUnion.html\">?<span>Documentation for featureunion-2: FeatureUnion</span></a></label><div class=\"sk-toggleable__content fitted\"><pre>FeatureUnion(transformer_list=[(&#x27;skiptransformer&#x27;, SkipTransformer()),\n",
       "                               (&#x27;passthrough&#x27;, Passthrough())])</pre></div> </div></div><div class=\"sk-parallel\"><div class=\"sk-parallel-item\"><div class=\"sk-item\"><div class=\"sk-label-container\"><div class=\"sk-label fitted sk-toggleable\"><label>skiptransformer</label></div></div><div class=\"sk-serial\"><div class=\"sk-item\"><div class=\"sk-estimator fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-8\" type=\"checkbox\" ><label for=\"sk-estimator-id-8\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">SkipTransformer</label><div class=\"sk-toggleable__content fitted\"><pre>SkipTransformer()</pre></div> </div></div></div></div></div><div class=\"sk-parallel-item\"><div class=\"sk-item\"><div class=\"sk-label-container\"><div class=\"sk-label fitted sk-toggleable\"><label>passthrough</label></div></div><div class=\"sk-serial\"><div class=\"sk-item\"><div class=\"sk-estimator fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-9\" type=\"checkbox\" ><label for=\"sk-estimator-id-9\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">Passthrough</label><div class=\"sk-toggleable__content fitted\"><pre>Passthrough()</pre></div> </div></div></div></div></div></div></div><div class=\"sk-item\"><div class=\"sk-estimator fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-10\" type=\"checkbox\" ><label for=\"sk-estimator-id-10\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">&nbsp;MLPClassifier<a class=\"sk-estimator-doc-link fitted\" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.5/modules/generated/sklearn.neural_network.MLPClassifier.html\">?<span>Documentation for MLPClassifier</span></a></label><div class=\"sk-toggleable__content fitted\"><pre>MLPClassifier(activation=&#x27;identity&#x27;, alpha=0.0023692590029,\n",
       "              hidden_layer_sizes=[139, 139], learning_rate=&#x27;invscaling&#x27;,\n",
       "              learning_rate_init=0.0004707733364, n_iter_no_change=32)</pre></div> </div></div></div></div></div></div>"
      ],
      "text/plain": [
       "Pipeline(steps=[('minmaxscaler', MinMaxScaler()),\n",
       "                ('selectpercentile',\n",
       "                 SelectPercentile(percentile=68.60012151662)),\n",
       "                ('featureunion-1',\n",
       "                 FeatureUnion(transformer_list=[('skiptransformer',\n",
       "                                                 SkipTransformer()),\n",
       "                                                ('passthrough',\n",
       "                                                 Passthrough())])),\n",
       "                ('featureunion-2',\n",
       "                 FeatureUnion(transformer_list=[('skiptransformer',\n",
       "                                                 SkipTransformer()),\n",
       "                                                ('passthrough',\n",
       "                                                 Passthrough())])),\n",
       "                ('mlpclassifier',\n",
       "                 MLPClassifier(activation='identity', alpha=0.0023692590029,\n",
       "                               hidden_layer_sizes=[139, 139],\n",
       "                               learning_rate='invscaling',\n",
       "                               learning_rate_init=0.0004707733364,\n",
       "                               n_iter_no_change=32))])"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "best_pipeline = est.fitted_pipeline_\n",
    "best_pipeline"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0,\n",
       "       0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0,\n",
       "       1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1,\n",
       "       1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1,\n",
       "       1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0,\n",
       "       1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1,\n",
       "       1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1])"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "best_pipeline.predict(X_test)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Saving the Pipeline\n",
    "\n",
    "We recommend using dill or pickle to save the instance of the fitted_pipeline_. Note that we do not recommend pickling the TPOT object itself."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "import dill as pickle\n",
    "with open(\"best_pipeline.pkl\", \"wb\") as f:\n",
    "    pickle.dump(best_pipeline, f)\n",
    "\n",
    "#load the pipeline\n",
    "import dill as pickle\n",
    "with open(\"best_pipeline.pkl\", \"rb\") as f:\n",
    "    my_loaded_best_pipeline = pickle.load(f)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## The evaluated_individuals Dataframe - Further analysis of results\n",
    "\n",
    "The `evaluated_individuals` attribute of the tpot estimator object is a Pandas Dataframe containing information about a run. Each row corresponds to an individual pipeline explored by tpot. The dataframe contains the following columns:\n",
    "\n",
    "| Column     | Description      |\n",
    "| :---        |    :----:   |\n",
    "| \\<n objective function columns\\>     |  The first set of columns will correspond to each objective function. These can either be automatically named by TPOT, or passed in by the user.    |\n",
    "| Parents  | This contains a tuple that contains the indexes of the 'parents' of the current pipeline. For example, (29, 42) means that the pipelines in indexes 29 and 42 were utilized to generate that pipeline. |\n",
    "| Variation_Function | The function applied to the parents to generate the new pipeline |\n",
    "| Individual | The individual class that represents a specific pipeline and hyperparameter configuration. This class also contains functions for mutation and crossover. To get the sklearn estimator/pipeline object from the individual you can call the `export_pipeline()` function. (as in, `pipe = ind.export_pipeline()`) |\n",
    "| Generation | The generation where the individual was created. (Note that the higher performing pipelines from previous generations may still be present in the current \"population\" of a given generation if selected.) |\n",
    "| Submitted Timestamp | Timestamp, in seconds, at which the pipeline was sent to be evaluated. This is the output of time.time(), which is \"Return the time in seconds since the epoch as a floating-point number. \" |\n",
    "| Completed Timestamp | Timestamp at which the pipeline evaluation completed in the same units as Submitted Timestamp |\n",
    "| Pareto_Front\t | If you have multiple parameters, this column is True if the pipeline performance fall on the pareto front line. This is the set of pipelines with scores that are strictly better than pipelines not on the line, but not strictly better than one another. |\n",
    "| Instance | This contains the unfitted pipeline evaluated for this row. (This is the pipeline returned by calling the export_pipeline() function of the individual class) |\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['roc_auc_score', 'complexity_scorer']"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#get the score/objective column names generated by TPOT\n",
    "est.objective_names"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>roc_auc_score</th>\n",
       "      <th>complexity_scorer</th>\n",
       "      <th>Parents</th>\n",
       "      <th>Variation_Function</th>\n",
       "      <th>Individual</th>\n",
       "      <th>Generation</th>\n",
       "      <th>Submitted Timestamp</th>\n",
       "      <th>Completed Timestamp</th>\n",
       "      <th>Eval Error</th>\n",
       "      <th>Pareto_Front</th>\n",
       "      <th>Instance</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>&lt;tpot.search_spaces.pipelines.sequential.Seque...</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.740178e+09</td>\n",
       "      <td>1.740178e+09</td>\n",
       "      <td>INVALID</td>\n",
       "      <td>NaN</td>\n",
       "      <td>(MaxAbsScaler(), RFE(estimator=ExtraTreesClass...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>&lt;tpot.search_spaces.pipelines.sequential.Seque...</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.740178e+09</td>\n",
       "      <td>1.740179e+09</td>\n",
       "      <td>INVALID</td>\n",
       "      <td>NaN</td>\n",
       "      <td>(RobustScaler(quantile_range=(0.1386847479391,...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>&lt;tpot.search_spaces.pipelines.sequential.Seque...</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.740178e+09</td>\n",
       "      <td>1.740178e+09</td>\n",
       "      <td>INVALID</td>\n",
       "      <td>NaN</td>\n",
       "      <td>(RobustScaler(quantile_range=(0.0087917518794,...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>&lt;tpot.search_spaces.pipelines.sequential.Seque...</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.740178e+09</td>\n",
       "      <td>1.740178e+09</td>\n",
       "      <td>INVALID</td>\n",
       "      <td>NaN</td>\n",
       "      <td>(Passthrough(), Passthrough(), FeatureUnion(tr...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>0.969262</td>\n",
       "      <td>241.2</td>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>&lt;tpot.search_spaces.pipelines.sequential.Seque...</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.740178e+09</td>\n",
       "      <td>1.740178e+09</td>\n",
       "      <td>None</td>\n",
       "      <td>NaN</td>\n",
       "      <td>(RobustScaler(quantile_range=(0.0359502923061,...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>...</th>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>245</th>\n",
       "      <td>0.986280</td>\n",
       "      <td>44.0</td>\n",
       "      <td>(184, 184)</td>\n",
       "      <td>ind_crossover</td>\n",
       "      <td>&lt;tpot.search_spaces.pipelines.sequential.Seque...</td>\n",
       "      <td>4.0</td>\n",
       "      <td>1.740179e+09</td>\n",
       "      <td>1.740179e+09</td>\n",
       "      <td>None</td>\n",
       "      <td>NaN</td>\n",
       "      <td>(RobustScaler(quantile_range=(0.1428289713161,...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>246</th>\n",
       "      <td>0.902845</td>\n",
       "      <td>9.0</td>\n",
       "      <td>(145, 148)</td>\n",
       "      <td>ind_mutate , ind_mutate , ind_crossover</td>\n",
       "      <td>&lt;tpot.search_spaces.pipelines.sequential.Seque...</td>\n",
       "      <td>4.0</td>\n",
       "      <td>1.740179e+09</td>\n",
       "      <td>1.740179e+09</td>\n",
       "      <td>None</td>\n",
       "      <td>NaN</td>\n",
       "      <td>(MinMaxScaler(), SelectFwe(alpha=0.00184795618...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>247</th>\n",
       "      <td>0.992851</td>\n",
       "      <td>5301.0</td>\n",
       "      <td>(155, 133)</td>\n",
       "      <td>ind_mutate , ind_mutate , ind_crossover</td>\n",
       "      <td>&lt;tpot.search_spaces.pipelines.sequential.Seque...</td>\n",
       "      <td>4.0</td>\n",
       "      <td>1.740179e+09</td>\n",
       "      <td>1.740179e+09</td>\n",
       "      <td>None</td>\n",
       "      <td>NaN</td>\n",
       "      <td>(MaxAbsScaler(), SelectFwe(alpha=0.00212090942...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>248</th>\n",
       "      <td>0.992349</td>\n",
       "      <td>7749.0</td>\n",
       "      <td>(152, 152)</td>\n",
       "      <td>ind_mutate</td>\n",
       "      <td>&lt;tpot.search_spaces.pipelines.sequential.Seque...</td>\n",
       "      <td>4.0</td>\n",
       "      <td>1.740179e+09</td>\n",
       "      <td>1.740179e+09</td>\n",
       "      <td>None</td>\n",
       "      <td>NaN</td>\n",
       "      <td>(MinMaxScaler(), SelectFromModel(estimator=Ext...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>249</th>\n",
       "      <td>0.515242</td>\n",
       "      <td>9.0</td>\n",
       "      <td>(182, 182)</td>\n",
       "      <td>ind_mutate</td>\n",
       "      <td>&lt;tpot.search_spaces.pipelines.sequential.Seque...</td>\n",
       "      <td>4.0</td>\n",
       "      <td>1.740179e+09</td>\n",
       "      <td>1.740179e+09</td>\n",
       "      <td>None</td>\n",
       "      <td>NaN</td>\n",
       "      <td>(MaxAbsScaler(), VarianceThreshold(threshold=0...</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>250 rows × 11 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "     roc_auc_score  complexity_scorer     Parents  \\\n",
       "0              NaN                NaN         NaN   \n",
       "1              NaN                NaN         NaN   \n",
       "2              NaN                NaN         NaN   \n",
       "3              NaN                NaN         NaN   \n",
       "4         0.969262              241.2         NaN   \n",
       "..             ...                ...         ...   \n",
       "245       0.986280               44.0  (184, 184)   \n",
       "246       0.902845                9.0  (145, 148)   \n",
       "247       0.992851             5301.0  (155, 133)   \n",
       "248       0.992349             7749.0  (152, 152)   \n",
       "249       0.515242                9.0  (182, 182)   \n",
       "\n",
       "                          Variation_Function  \\\n",
       "0                                        NaN   \n",
       "1                                        NaN   \n",
       "2                                        NaN   \n",
       "3                                        NaN   \n",
       "4                                        NaN   \n",
       "..                                       ...   \n",
       "245                            ind_crossover   \n",
       "246  ind_mutate , ind_mutate , ind_crossover   \n",
       "247  ind_mutate , ind_mutate , ind_crossover   \n",
       "248                               ind_mutate   \n",
       "249                               ind_mutate   \n",
       "\n",
       "                                            Individual  Generation  \\\n",
       "0    <tpot.search_spaces.pipelines.sequential.Seque...         0.0   \n",
       "1    <tpot.search_spaces.pipelines.sequential.Seque...         0.0   \n",
       "2    <tpot.search_spaces.pipelines.sequential.Seque...         0.0   \n",
       "3    <tpot.search_spaces.pipelines.sequential.Seque...         0.0   \n",
       "4    <tpot.search_spaces.pipelines.sequential.Seque...         0.0   \n",
       "..                                                 ...         ...   \n",
       "245  <tpot.search_spaces.pipelines.sequential.Seque...         4.0   \n",
       "246  <tpot.search_spaces.pipelines.sequential.Seque...         4.0   \n",
       "247  <tpot.search_spaces.pipelines.sequential.Seque...         4.0   \n",
       "248  <tpot.search_spaces.pipelines.sequential.Seque...         4.0   \n",
       "249  <tpot.search_spaces.pipelines.sequential.Seque...         4.0   \n",
       "\n",
       "     Submitted Timestamp  Completed Timestamp Eval Error  Pareto_Front  \\\n",
       "0           1.740178e+09         1.740178e+09    INVALID           NaN   \n",
       "1           1.740178e+09         1.740179e+09    INVALID           NaN   \n",
       "2           1.740178e+09         1.740178e+09    INVALID           NaN   \n",
       "3           1.740178e+09         1.740178e+09    INVALID           NaN   \n",
       "4           1.740178e+09         1.740178e+09       None           NaN   \n",
       "..                   ...                  ...        ...           ...   \n",
       "245         1.740179e+09         1.740179e+09       None           NaN   \n",
       "246         1.740179e+09         1.740179e+09       None           NaN   \n",
       "247         1.740179e+09         1.740179e+09       None           NaN   \n",
       "248         1.740179e+09         1.740179e+09       None           NaN   \n",
       "249         1.740179e+09         1.740179e+09       None           NaN   \n",
       "\n",
       "                                              Instance  \n",
       "0    (MaxAbsScaler(), RFE(estimator=ExtraTreesClass...  \n",
       "1    (RobustScaler(quantile_range=(0.1386847479391,...  \n",
       "2    (RobustScaler(quantile_range=(0.0087917518794,...  \n",
       "3    (Passthrough(), Passthrough(), FeatureUnion(tr...  \n",
       "4    (RobustScaler(quantile_range=(0.0359502923061,...  \n",
       "..                                                 ...  \n",
       "245  (RobustScaler(quantile_range=(0.1428289713161,...  \n",
       "246  (MinMaxScaler(), SelectFwe(alpha=0.00184795618...  \n",
       "247  (MaxAbsScaler(), SelectFwe(alpha=0.00212090942...  \n",
       "248  (MinMaxScaler(), SelectFromModel(estimator=Ext...  \n",
       "249  (MaxAbsScaler(), VarianceThreshold(threshold=0...  \n",
       "\n",
       "[250 rows x 11 columns]"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df = est.evaluated_individuals\n",
    "df"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Let's plot the performances of the different pipelines, including the Pareto front\n",
    "\n",
    "Plotting the performance of multiple objectives in a scatterplot is a helpful way to visualize the tradeoff between model complexity and predictive performance. This is best visualized when plotting the Pareto front pipelines, which present the best-performing pipeline along the spectrum of complexity. Generally, higher complexity models may yield higher performance but be more difficult to interpret. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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33+XqlSpIr366quSO7/uCRMmSFWrVpU2b94sXbhwQfrkk0+kwMBA6ciRI5Ir+fXXX6W33npL2rBhA88EkX788UeDx1+8eFEKDg6Wxo8fL506dUpatGiR+DvfsmWLxdeCwOmG2rRpIyUkJCjvl5SUiH84s2fP1nk8f2CGh4dLnva6P/30U6lWrVqSQqGQPOl1a/r444+lcuXKSTk5OZI7v24+tlu3bmrb+EM1Pj5ecufXzV8OFi9erLbtsccek5555hnJVZERgZO/MDRs2FBt26BBg6SePXta/PzoqnUzCoWCDh8+TD169FCrx8v3Dxw4oPdxOTk5FBsbK7qv+vXrR//88w+5++v++eefqV27dqKrtlKlStSoUSOaNWsWlZSUkLv/vlV9+eWX9NRTT1FISAi58+tu3769eIzcrXnx4kXRLd+7d29y59ddWFioNfTCXdV79+4ld3bgwAG194n17NnT6H8XhiBwupm0tDTxwc+BQBXfv3nzps7H8JjP8uXL6aeffqJvvvlGrBjDHzJXr14ld37d/MG5bt068Tj+AH3nnXdo7ty5NGPGDHLn162Kg8jJkyfphRdeIFdizut++umnxVhfhw4dyM/PT4z58Xjvm2++Se78ujlYzJs3j86fPy/+bf/vf/+jDRs20I0bN8id3bx5U+f7xCuo5OfnW3RuBE4Qra7nn3+emjVrRp07dxb/qCpUqECff/45uTP+EKlYsSJ98cUX1LJlSxo0aBC99dZb9Nlnn5Gn4NZm48aNqU2bNuTuOFGEexQ++eQTOnLkiPg737x5M7333nvkzhYsWEB169al+++/n/z9/Wn06NE0bNiwMpfOAv08flkxdxMdHU0+Pj5069Ytte18v3Llykadg7+NN2/enJKSksidXzdn0vJr5cfJ6tevL76pcpcYf8i48+87NzeX1qxZI1phrsac1809Cs8995yydc1fGPg9ePHFF8UXJlcIJOa8bv4SzBmonD2cnp4uslInTZpEtWrVIndWuXJlne8TLzfGXdWWcP6/FDAJf9hz62nbtm1qLSu+zy1LY3BX0IkTJ0RgcefXHR8fL74c8HGyc+fOidftCkHT0t/32rVrxfjXs88+S67GnNedl5enFRzlL02uUrLbkt83j3NWq1aNiouLaf369SKXwZ21a9dO7X1i3E1t7OegQRanF4FTpqtz2v3KlStF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",
      "text/plain": [
       "<Figure size 500x500 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "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aJdO3X/9/OhrqN09U7qf+mWEa/hXjpY4pV+CaCuUhh/9AkB/T8l7M6XHLCXHHUAQsAAgwEycONHS/74WL15snTp1yjp06JA1depUK0uWLFb69Omtw4cPW3/++aeVNm1aa+DAgW6P3bZtmxUeHu42/YknnjDrGzt27E3P1bx5cys6Otpt2ubNm83yr776qtv0bt26mek//fSTc1qBAgXMtPnz57stu3TpUjO9ZMmS1tWrV53TGzdubIWFhVl16tRxWz4+Pt6sy9XFixdv2t7atWtbhQsXdpvm2Iaff/7ZOe3kyZNWZGSk1bVrV+e0vn37muVmzpx503pv3Lhhfn755ZdWmjRprBUrVrjN19dOH7ty5UrrVnQ/s2fPbvb50qVLzulz5swxj9XnT3qM169fb91J/fr1rYiICGvfvn3OaUePHrUyZsxoPf744zets0aNGs79UZ07dzbvlTNnzri9J/TmsGDBAvPYefPmuT136dKl3ZZLjr5H9bH9+vVL9v2l8wYMGOA2vVy5claFChWc9/X11uWmTJnitpy+r5KbnpQ+ty63Z88esz379++3xo0bZ94DOXLksC5cuGBdv37dunLlitvjTp8+bea/8sorzmn6WF1XbGyseR+5ql69ulWqVCnr8uXLzmn6WlepUsUqWrSoc9r06dPNOvRz4ErXp8eyVq1aVmJionP6p59+apafMGHCbffTcYyTu7l+7vQz4vr5sfPeTOkxu91xBxAc6NoHIGDpN9b6za92LdOWJG050NYg/TZcx31o64m2Ruk3045bzpw5pWjRojd1V9Jv1PXb6JTQghZKu4e50pYpNXfuXLfp2qKi314nR7vNuX7Drt9wawuHtgi50unaJVG7FDq4jrNyfAP/xBNPyB9//GHuu9Juf9pq46Cvm7b66LIO2mJQpkwZ8w16Uo5CBNr1SVsOHnroIbfX1dFFLOnr6mrDhg1m7JO2mmmXL4e6deua9SV93VJCWwa0aIK2eGh3RYdcuXLJv//9b9NylrQqnbaguBZW0NdF13PgwIHbvte0i9mUKVOc07SoydatW00L5t3S8UqudJtcj42+7trdrmbNmm6vu7Yo6vv+dq+7Kz3meuz1PaktXNo9U1937d6mLW2OMU762fnnn3/M+01bZjdt2nTTunR8laP7mtLltXCFfua0Ncuxjdraq+//vXv33tRNLilt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",
      "text/plain": [
       "<Figure size 1000x500 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import seaborn as sns\n",
    "import matplotlib.pyplot as plt\n",
    "#replace nans in pareto front with 0\n",
    "fig, ax = plt.subplots(figsize=(5,5))\n",
    "sns.scatterplot(df[df['Pareto_Front']!=1], x='roc_auc_score', y='complexity_scorer', label='other', ax=ax)\n",
    "sns.scatterplot(df[df['Pareto_Front']==1], x='roc_auc_score', y='complexity_scorer', label='Pareto Front', ax=ax)\n",
    "ax.title.set_text('Performance of all pipelines')\n",
    "#log scale y\n",
    "ax.set_yscale('log')\n",
    "plt.show()\n",
    "\n",
    "#replace nans in pareto front with 0\n",
    "fig, ax = plt.subplots(figsize=(10,5))\n",
    "sns.scatterplot(df[df['Pareto_Front']==1], x='roc_auc_score', y='complexity_scorer', label='Pareto Front', ax=ax)\n",
    "ax.title.set_text('Performance of only the Pareto Front')\n",
    "#log scale y\n",
    "# ax.set_yscale('log')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>roc_auc_score</th>\n",
       "      <th>complexity_scorer</th>\n",
       "      <th>Parents</th>\n",
       "      <th>Variation_Function</th>\n",
       "      <th>Individual</th>\n",
       "      <th>Generation</th>\n",
       "      <th>Submitted Timestamp</th>\n",
       "      <th>Completed Timestamp</th>\n",
       "      <th>Eval Error</th>\n",
       "      <th>Pareto_Front</th>\n",
       "      <th>Instance</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>51</th>\n",
       "      <td>0.996818</td>\n",
       "      <td>582.0</td>\n",
       "      <td>(13, 13)</td>\n",
       "      <td>ind_mutate</td>\n",
       "      <td>&lt;tpot.search_spaces.pipelines.sequential.Seque...</td>\n",
       "      <td>1.0</td>\n",
       "      <td>1.740179e+09</td>\n",
       "      <td>1.740179e+09</td>\n",
       "      <td>None</td>\n",
       "      <td>1.0</td>\n",
       "      <td>(MinMaxScaler(), SelectPercentile(percentile=6...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>133</th>\n",
       "      <td>0.996239</td>\n",
       "      <td>31.0</td>\n",
       "      <td>(65, 65)</td>\n",
       "      <td>ind_mutate</td>\n",
       "      <td>&lt;tpot.search_spaces.pipelines.sequential.Seque...</td>\n",
       "      <td>2.0</td>\n",
       "      <td>1.740179e+09</td>\n",
       "      <td>1.740179e+09</td>\n",
       "      <td>None</td>\n",
       "      <td>1.0</td>\n",
       "      <td>(StandardScaler(), SelectFwe(alpha=0.002276474...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>185</th>\n",
       "      <td>0.995843</td>\n",
       "      <td>30.9</td>\n",
       "      <td>(133, 133)</td>\n",
       "      <td>ind_mutate</td>\n",
       "      <td>&lt;tpot.search_spaces.pipelines.sequential.Seque...</td>\n",
       "      <td>3.0</td>\n",
       "      <td>1.740179e+09</td>\n",
       "      <td>1.740179e+09</td>\n",
       "      <td>None</td>\n",
       "      <td>1.0</td>\n",
       "      <td>(StandardScaler(), SelectFwe(alpha=0.000234016...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>233</th>\n",
       "      <td>0.995115</td>\n",
       "      <td>30.7</td>\n",
       "      <td>(185, 185)</td>\n",
       "      <td>ind_mutate</td>\n",
       "      <td>&lt;tpot.search_spaces.pipelines.sequential.Seque...</td>\n",
       "      <td>4.0</td>\n",
       "      <td>1.740179e+09</td>\n",
       "      <td>1.740179e+09</td>\n",
       "      <td>None</td>\n",
       "      <td>1.0</td>\n",
       "      <td>(StandardScaler(), SelectFwe(alpha=0.000234016...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>85</th>\n",
       "      <td>0.990894</td>\n",
       "      <td>26.0</td>\n",
       "      <td>(6, 23)</td>\n",
       "      <td>ind_crossover</td>\n",
       "      <td>&lt;tpot.search_spaces.pipelines.sequential.Seque...</td>\n",
       "      <td>1.0</td>\n",
       "      <td>1.740179e+09</td>\n",
       "      <td>1.740179e+09</td>\n",
       "      <td>None</td>\n",
       "      <td>1.0</td>\n",
       "      <td>(MaxAbsScaler(), SelectFwe(alpha=0.00114277554...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>228</th>\n",
       "      <td>0.990081</td>\n",
       "      <td>19.0</td>\n",
       "      <td>(162, 162)</td>\n",
       "      <td>ind_mutate</td>\n",
       "      <td>&lt;tpot.search_spaces.pipelines.sequential.Seque...</td>\n",
       "      <td>4.0</td>\n",
       "      <td>1.740179e+09</td>\n",
       "      <td>1.740179e+09</td>\n",
       "      <td>None</td>\n",
       "      <td>1.0</td>\n",
       "      <td>(MaxAbsScaler(), VarianceThreshold(threshold=0...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>215</th>\n",
       "      <td>0.988614</td>\n",
       "      <td>9.0</td>\n",
       "      <td>(162, 162)</td>\n",
       "      <td>ind_mutate</td>\n",
       "      <td>&lt;tpot.search_spaces.pipelines.sequential.Seque...</td>\n",
       "      <td>4.0</td>\n",
       "      <td>1.740179e+09</td>\n",
       "      <td>1.740179e+09</td>\n",
       "      <td>None</td>\n",
       "      <td>1.0</td>\n",
       "      <td>(MaxAbsScaler(), VarianceThreshold(threshold=0...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>121</th>\n",
       "      <td>0.982524</td>\n",
       "      <td>7.0</td>\n",
       "      <td>(10, 10)</td>\n",
       "      <td>ind_mutate</td>\n",
       "      <td>&lt;tpot.search_spaces.pipelines.sequential.Seque...</td>\n",
       "      <td>2.0</td>\n",
       "      <td>1.740179e+09</td>\n",
       "      <td>1.740179e+09</td>\n",
       "      <td>None</td>\n",
       "      <td>1.0</td>\n",
       "      <td>(MaxAbsScaler(), SelectFwe(alpha=0.03019980124...</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     roc_auc_score  complexity_scorer     Parents Variation_Function  \\\n",
       "51        0.996818              582.0    (13, 13)         ind_mutate   \n",
       "133       0.996239               31.0    (65, 65)         ind_mutate   \n",
       "185       0.995843               30.9  (133, 133)         ind_mutate   \n",
       "233       0.995115               30.7  (185, 185)         ind_mutate   \n",
       "85        0.990894               26.0     (6, 23)      ind_crossover   \n",
       "228       0.990081               19.0  (162, 162)         ind_mutate   \n",
       "215       0.988614                9.0  (162, 162)         ind_mutate   \n",
       "121       0.982524                7.0    (10, 10)         ind_mutate   \n",
       "\n",
       "                                            Individual  Generation  \\\n",
       "51   <tpot.search_spaces.pipelines.sequential.Seque...         1.0   \n",
       "133  <tpot.search_spaces.pipelines.sequential.Seque...         2.0   \n",
       "185  <tpot.search_spaces.pipelines.sequential.Seque...         3.0   \n",
       "233  <tpot.search_spaces.pipelines.sequential.Seque...         4.0   \n",
       "85   <tpot.search_spaces.pipelines.sequential.Seque...         1.0   \n",
       "228  <tpot.search_spaces.pipelines.sequential.Seque...         4.0   \n",
       "215  <tpot.search_spaces.pipelines.sequential.Seque...         4.0   \n",
       "121  <tpot.search_spaces.pipelines.sequential.Seque...         2.0   \n",
       "\n",
       "     Submitted Timestamp  Completed Timestamp Eval Error  Pareto_Front  \\\n",
       "51          1.740179e+09         1.740179e+09       None           1.0   \n",
       "133         1.740179e+09         1.740179e+09       None           1.0   \n",
       "185         1.740179e+09         1.740179e+09       None           1.0   \n",
       "233         1.740179e+09         1.740179e+09       None           1.0   \n",
       "85          1.740179e+09         1.740179e+09       None           1.0   \n",
       "228         1.740179e+09         1.740179e+09       None           1.0   \n",
       "215         1.740179e+09         1.740179e+09       None           1.0   \n",
       "121         1.740179e+09         1.740179e+09       None           1.0   \n",
       "\n",
       "                                              Instance  \n",
       "51   (MinMaxScaler(), SelectPercentile(percentile=6...  \n",
       "133  (StandardScaler(), SelectFwe(alpha=0.002276474...  \n",
       "185  (StandardScaler(), SelectFwe(alpha=0.000234016...  \n",
       "233  (StandardScaler(), SelectFwe(alpha=0.000234016...  \n",
       "85   (MaxAbsScaler(), SelectFwe(alpha=0.00114277554...  \n",
       "228  (MaxAbsScaler(), VarianceThreshold(threshold=0...  \n",
       "215  (MaxAbsScaler(), VarianceThreshold(threshold=0...  \n",
       "121  (MaxAbsScaler(), SelectFwe(alpha=0.03019980124...  "
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#plot only the pareto front pipelines\n",
    "sorted_pareto_front = df[df['Pareto_Front']==1].sort_values('roc_auc_score', ascending=False)\n",
    "sorted_pareto_front"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In some cases, you may want to select a slightly lower performing pipeline that is signficantly less complex."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>#sk-container-id-2 {\n",
       "  /* Definition of color scheme common for light and dark mode */\n",
       "  --sklearn-color-text: black;\n",
       "  --sklearn-color-line: gray;\n",
       "  /* Definition of color scheme for unfitted estimators */\n",
       "  --sklearn-color-unfitted-level-0: #fff5e6;\n",
       "  --sklearn-color-unfitted-level-1: #f6e4d2;\n",
       "  --sklearn-color-unfitted-level-2: #ffe0b3;\n",
       "  --sklearn-color-unfitted-level-3: chocolate;\n",
       "  /* Definition of color scheme for fitted estimators */\n",
       "  --sklearn-color-fitted-level-0: #f0f8ff;\n",
       "  --sklearn-color-fitted-level-1: #d4ebff;\n",
       "  --sklearn-color-fitted-level-2: #b3dbfd;\n",
       "  --sklearn-color-fitted-level-3: cornflowerblue;\n",
       "\n",
       "  /* Specific color for light theme */\n",
       "  --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
       "  --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, white)));\n",
       "  --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
       "  --sklearn-color-icon: #696969;\n",
       "\n",
       "  @media (prefers-color-scheme: dark) {\n",
       "    /* Redefinition of color scheme for dark theme */\n",
       "    --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
       "    --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, #111)));\n",
       "    --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
       "    --sklearn-color-icon: #878787;\n",
       "  }\n",
       "}\n",
       "\n",
       "#sk-container-id-2 {\n",
       "  color: var(--sklearn-color-text);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 pre {\n",
       "  padding: 0;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 input.sk-hidden--visually {\n",
       "  border: 0;\n",
       "  clip: rect(1px 1px 1px 1px);\n",
       "  clip: rect(1px, 1px, 1px, 1px);\n",
       "  height: 1px;\n",
       "  margin: -1px;\n",
       "  overflow: hidden;\n",
       "  padding: 0;\n",
       "  position: absolute;\n",
       "  width: 1px;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-dashed-wrapped {\n",
       "  border: 1px dashed var(--sklearn-color-line);\n",
       "  margin: 0 0.4em 0.5em 0.4em;\n",
       "  box-sizing: border-box;\n",
       "  padding-bottom: 0.4em;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-container {\n",
       "  /* jupyter's `normalize.less` sets `[hidden] { display: none; }`\n",
       "     but bootstrap.min.css set `[hidden] { display: none !important; }`\n",
       "     so we also need the `!important` here to be able to override the\n",
       "     default hidden behavior on the sphinx rendered scikit-learn.org.\n",
       "     See: https://github.com/scikit-learn/scikit-learn/issues/21755 */\n",
       "  display: inline-block !important;\n",
       "  position: relative;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-text-repr-fallback {\n",
       "  display: none;\n",
       "}\n",
       "\n",
       "div.sk-parallel-item,\n",
       "div.sk-serial,\n",
       "div.sk-item {\n",
       "  /* draw centered vertical line to link estimators */\n",
       "  background-image: linear-gradient(var(--sklearn-color-text-on-default-background), var(--sklearn-color-text-on-default-background));\n",
       "  background-size: 2px 100%;\n",
       "  background-repeat: no-repeat;\n",
       "  background-position: center center;\n",
       "}\n",
       "\n",
       "/* Parallel-specific style estimator block */\n",
       "\n",
       "#sk-container-id-2 div.sk-parallel-item::after {\n",
       "  content: \"\";\n",
       "  width: 100%;\n",
       "  border-bottom: 2px solid var(--sklearn-color-text-on-default-background);\n",
       "  flex-grow: 1;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-parallel {\n",
       "  display: flex;\n",
       "  align-items: stretch;\n",
       "  justify-content: center;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  position: relative;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-parallel-item {\n",
       "  display: flex;\n",
       "  flex-direction: column;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-parallel-item:first-child::after {\n",
       "  align-self: flex-end;\n",
       "  width: 50%;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-parallel-item:last-child::after {\n",
       "  align-self: flex-start;\n",
       "  width: 50%;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-parallel-item:only-child::after {\n",
       "  width: 0;\n",
       "}\n",
       "\n",
       "/* Serial-specific style estimator block */\n",
       "\n",
       "#sk-container-id-2 div.sk-serial {\n",
       "  display: flex;\n",
       "  flex-direction: column;\n",
       "  align-items: center;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  padding-right: 1em;\n",
       "  padding-left: 1em;\n",
       "}\n",
       "\n",
       "\n",
       "/* Toggleable style: style used for estimator/Pipeline/ColumnTransformer box that is\n",
       "clickable and can be expanded/collapsed.\n",
       "- Pipeline and ColumnTransformer use this feature and define the default style\n",
       "- Estimators will overwrite some part of the style using the `sk-estimator` class\n",
       "*/\n",
       "\n",
       "/* Pipeline and ColumnTransformer style (default) */\n",
       "\n",
       "#sk-container-id-2 div.sk-toggleable {\n",
       "  /* Default theme specific background. It is overwritten whether we have a\n",
       "  specific estimator or a Pipeline/ColumnTransformer */\n",
       "  background-color: var(--sklearn-color-background);\n",
       "}\n",
       "\n",
       "/* Toggleable label */\n",
       "#sk-container-id-2 label.sk-toggleable__label {\n",
       "  cursor: pointer;\n",
       "  display: block;\n",
       "  width: 100%;\n",
       "  margin-bottom: 0;\n",
       "  padding: 0.5em;\n",
       "  box-sizing: border-box;\n",
       "  text-align: center;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 label.sk-toggleable__label-arrow:before {\n",
       "  /* Arrow on the left of the label */\n",
       "  content: \"▸\";\n",
       "  float: left;\n",
       "  margin-right: 0.25em;\n",
       "  color: var(--sklearn-color-icon);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 label.sk-toggleable__label-arrow:hover:before {\n",
       "  color: var(--sklearn-color-text);\n",
       "}\n",
       "\n",
       "/* Toggleable content - dropdown */\n",
       "\n",
       "#sk-container-id-2 div.sk-toggleable__content {\n",
       "  max-height: 0;\n",
       "  max-width: 0;\n",
       "  overflow: hidden;\n",
       "  text-align: left;\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-toggleable__content.fitted {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-toggleable__content pre {\n",
       "  margin: 0.2em;\n",
       "  border-radius: 0.25em;\n",
       "  color: var(--sklearn-color-text);\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-toggleable__content.fitted pre {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 input.sk-toggleable__control:checked~div.sk-toggleable__content {\n",
       "  /* Expand drop-down */\n",
       "  max-height: 200px;\n",
       "  max-width: 100%;\n",
       "  overflow: auto;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 input.sk-toggleable__control:checked~label.sk-toggleable__label-arrow:before {\n",
       "  content: \"▾\";\n",
       "}\n",
       "\n",
       "/* Pipeline/ColumnTransformer-specific style */\n",
       "\n",
       "#sk-container-id-2 div.sk-label input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-label.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Estimator-specific style */\n",
       "\n",
       "/* Colorize estimator box */\n",
       "#sk-container-id-2 div.sk-estimator input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-estimator.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-label label.sk-toggleable__label,\n",
       "#sk-container-id-2 div.sk-label label {\n",
       "  /* The background is the default theme color */\n",
       "  color: var(--sklearn-color-text-on-default-background);\n",
       "}\n",
       "\n",
       "/* On hover, darken the color of the background */\n",
       "#sk-container-id-2 div.sk-label:hover label.sk-toggleable__label {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "/* Label box, darken color on hover, fitted */\n",
       "#sk-container-id-2 div.sk-label.fitted:hover label.sk-toggleable__label.fitted {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Estimator label */\n",
       "\n",
       "#sk-container-id-2 div.sk-label label {\n",
       "  font-family: monospace;\n",
       "  font-weight: bold;\n",
       "  display: inline-block;\n",
       "  line-height: 1.2em;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-label-container {\n",
       "  text-align: center;\n",
       "}\n",
       "\n",
       "/* Estimator-specific */\n",
       "#sk-container-id-2 div.sk-estimator {\n",
       "  font-family: monospace;\n",
       "  border: 1px dotted var(--sklearn-color-border-box);\n",
       "  border-radius: 0.25em;\n",
       "  box-sizing: border-box;\n",
       "  margin-bottom: 0.5em;\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-estimator.fitted {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "/* on hover */\n",
       "#sk-container-id-2 div.sk-estimator:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-estimator.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Specification for estimator info (e.g. \"i\" and \"?\") */\n",
       "\n",
       "/* Common style for \"i\" and \"?\" */\n",
       "\n",
       ".sk-estimator-doc-link,\n",
       "a:link.sk-estimator-doc-link,\n",
       "a:visited.sk-estimator-doc-link {\n",
       "  float: right;\n",
       "  font-size: smaller;\n",
       "  line-height: 1em;\n",
       "  font-family: monospace;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  border-radius: 1em;\n",
       "  height: 1em;\n",
       "  width: 1em;\n",
       "  text-decoration: none !important;\n",
       "  margin-left: 1ex;\n",
       "  /* unfitted */\n",
       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-unfitted-level-1);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link.fitted,\n",
       "a:link.sk-estimator-doc-link.fitted,\n",
       "a:visited.sk-estimator-doc-link.fitted {\n",
       "  /* fitted */\n",
       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-fitted-level-1);\n",
       "}\n",
       "\n",
       "/* On hover */\n",
       "div.sk-estimator:hover .sk-estimator-doc-link:hover,\n",
       ".sk-estimator-doc-link:hover,\n",
       "div.sk-label-container:hover .sk-estimator-doc-link:hover,\n",
       ".sk-estimator-doc-link:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "div.sk-estimator.fitted:hover .sk-estimator-doc-link.fitted:hover,\n",
       ".sk-estimator-doc-link.fitted:hover,\n",
       "div.sk-label-container:hover .sk-estimator-doc-link.fitted:hover,\n",
       ".sk-estimator-doc-link.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "/* Span, style for the box shown on hovering the info icon */\n",
       ".sk-estimator-doc-link span {\n",
       "  display: none;\n",
       "  z-index: 9999;\n",
       "  position: relative;\n",
       "  font-weight: normal;\n",
       "  right: .2ex;\n",
       "  padding: .5ex;\n",
       "  margin: .5ex;\n",
       "  width: min-content;\n",
       "  min-width: 20ex;\n",
       "  max-width: 50ex;\n",
       "  color: var(--sklearn-color-text);\n",
       "  box-shadow: 2pt 2pt 4pt #999;\n",
       "  /* unfitted */\n",
       "  background: var(--sklearn-color-unfitted-level-0);\n",
       "  border: .5pt solid var(--sklearn-color-unfitted-level-3);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link.fitted span {\n",
       "  /* fitted */\n",
       "  background: var(--sklearn-color-fitted-level-0);\n",
       "  border: var(--sklearn-color-fitted-level-3);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link:hover span {\n",
       "  display: block;\n",
       "}\n",
       "\n",
       "/* \"?\"-specific style due to the `<a>` HTML tag */\n",
       "\n",
       "#sk-container-id-2 a.estimator_doc_link {\n",
       "  float: right;\n",
       "  font-size: 1rem;\n",
       "  line-height: 1em;\n",
       "  font-family: monospace;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  border-radius: 1rem;\n",
       "  height: 1rem;\n",
       "  width: 1rem;\n",
       "  text-decoration: none;\n",
       "  /* unfitted */\n",
       "  color: var(--sklearn-color-unfitted-level-1);\n",
       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 a.estimator_doc_link.fitted {\n",
       "  /* fitted */\n",
       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-fitted-level-1);\n",
       "}\n",
       "\n",
       "/* On hover */\n",
       "#sk-container-id-2 a.estimator_doc_link:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 a.estimator_doc_link.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-3);\n",
       "}\n",
       "</style><div id=\"sk-container-id-2\" class=\"sk-top-container\"><div class=\"sk-text-repr-fallback\"><pre>Pipeline(steps=[(&#x27;maxabsscaler&#x27;, MaxAbsScaler()),\n",
       "                (&#x27;selectfwe&#x27;, SelectFwe(alpha=0.0301998012478)),\n",
       "                (&#x27;featureunion-1&#x27;,\n",
       "                 FeatureUnion(transformer_list=[(&#x27;skiptransformer&#x27;,\n",
       "                                                 SkipTransformer()),\n",
       "                                                (&#x27;passthrough&#x27;,\n",
       "                                                 Passthrough())])),\n",
       "                (&#x27;featureunion-2&#x27;,\n",
       "                 FeatureUnion(transformer_list=[(&#x27;skiptransformer&#x27;,\n",
       "                                                 SkipTransformer()),\n",
       "                                                (&#x27;passthrough&#x27;,\n",
       "                                                 Passthrough())])),\n",
       "                (&#x27;kneighborsclassifier&#x27;,\n",
       "                 KNeighborsClassifier(n_jobs=1, n_neighbors=2))])</pre><b>In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. <br />On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.</b></div><div class=\"sk-container\" hidden><div class=\"sk-item sk-dashed-wrapped\"><div class=\"sk-label-container\"><div class=\"sk-label  sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-11\" type=\"checkbox\" ><label for=\"sk-estimator-id-11\" class=\"sk-toggleable__label  sk-toggleable__label-arrow \">&nbsp;&nbsp;Pipeline<a class=\"sk-estimator-doc-link \" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.5/modules/generated/sklearn.pipeline.Pipeline.html\">?<span>Documentation for Pipeline</span></a><span class=\"sk-estimator-doc-link \">i<span>Not fitted</span></span></label><div class=\"sk-toggleable__content \"><pre>Pipeline(steps=[(&#x27;maxabsscaler&#x27;, MaxAbsScaler()),\n",
       "                (&#x27;selectfwe&#x27;, SelectFwe(alpha=0.0301998012478)),\n",
       "                (&#x27;featureunion-1&#x27;,\n",
       "                 FeatureUnion(transformer_list=[(&#x27;skiptransformer&#x27;,\n",
       "                                                 SkipTransformer()),\n",
       "                                                (&#x27;passthrough&#x27;,\n",
       "                                                 Passthrough())])),\n",
       "                (&#x27;featureunion-2&#x27;,\n",
       "                 FeatureUnion(transformer_list=[(&#x27;skiptransformer&#x27;,\n",
       "                                                 SkipTransformer()),\n",
       "                                                (&#x27;passthrough&#x27;,\n",
       "                                                 Passthrough())])),\n",
       "                (&#x27;kneighborsclassifier&#x27;,\n",
       "                 KNeighborsClassifier(n_jobs=1, n_neighbors=2))])</pre></div> </div></div><div class=\"sk-serial\"><div class=\"sk-item\"><div class=\"sk-estimator  sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-12\" type=\"checkbox\" ><label for=\"sk-estimator-id-12\" class=\"sk-toggleable__label  sk-toggleable__label-arrow \">&nbsp;MaxAbsScaler<a class=\"sk-estimator-doc-link \" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.5/modules/generated/sklearn.preprocessing.MaxAbsScaler.html\">?<span>Documentation for MaxAbsScaler</span></a></label><div class=\"sk-toggleable__content \"><pre>MaxAbsScaler()</pre></div> </div></div><div class=\"sk-item\"><div class=\"sk-estimator  sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-13\" type=\"checkbox\" ><label for=\"sk-estimator-id-13\" class=\"sk-toggleable__label  sk-toggleable__label-arrow \">&nbsp;SelectFwe<a class=\"sk-estimator-doc-link \" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.5/modules/generated/sklearn.feature_selection.SelectFwe.html\">?<span>Documentation for SelectFwe</span></a></label><div class=\"sk-toggleable__content \"><pre>SelectFwe(alpha=0.0301998012478)</pre></div> </div></div><div class=\"sk-item sk-dashed-wrapped\"><div class=\"sk-label-container\"><div class=\"sk-label  sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-14\" type=\"checkbox\" ><label for=\"sk-estimator-id-14\" class=\"sk-toggleable__label  sk-toggleable__label-arrow \">&nbsp;featureunion-1: FeatureUnion<a class=\"sk-estimator-doc-link \" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.5/modules/generated/sklearn.pipeline.FeatureUnion.html\">?<span>Documentation for featureunion-1: FeatureUnion</span></a></label><div class=\"sk-toggleable__content \"><pre>FeatureUnion(transformer_list=[(&#x27;skiptransformer&#x27;, SkipTransformer()),\n",
       "                               (&#x27;passthrough&#x27;, Passthrough())])</pre></div> </div></div><div class=\"sk-parallel\"><div class=\"sk-parallel-item\"><div class=\"sk-item\"><div class=\"sk-label-container\"><div class=\"sk-label  sk-toggleable\"><label>skiptransformer</label></div></div><div class=\"sk-serial\"><div class=\"sk-item\"><div class=\"sk-estimator  sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-15\" type=\"checkbox\" ><label for=\"sk-estimator-id-15\" class=\"sk-toggleable__label  sk-toggleable__label-arrow \">SkipTransformer</label><div class=\"sk-toggleable__content \"><pre>SkipTransformer()</pre></div> </div></div></div></div></div><div class=\"sk-parallel-item\"><div class=\"sk-item\"><div class=\"sk-label-container\"><div class=\"sk-label  sk-toggleable\"><label>passthrough</label></div></div><div class=\"sk-serial\"><div class=\"sk-item\"><div class=\"sk-estimator  sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-16\" type=\"checkbox\" ><label for=\"sk-estimator-id-16\" class=\"sk-toggleable__label  sk-toggleable__label-arrow \">Passthrough</label><div class=\"sk-toggleable__content \"><pre>Passthrough()</pre></div> </div></div></div></div></div></div></div><div class=\"sk-item sk-dashed-wrapped\"><div class=\"sk-label-container\"><div class=\"sk-label  sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-17\" type=\"checkbox\" ><label for=\"sk-estimator-id-17\" class=\"sk-toggleable__label  sk-toggleable__label-arrow \">&nbsp;featureunion-2: FeatureUnion<a class=\"sk-estimator-doc-link \" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.5/modules/generated/sklearn.pipeline.FeatureUnion.html\">?<span>Documentation for featureunion-2: FeatureUnion</span></a></label><div class=\"sk-toggleable__content \"><pre>FeatureUnion(transformer_list=[(&#x27;skiptransformer&#x27;, SkipTransformer()),\n",
       "                               (&#x27;passthrough&#x27;, Passthrough())])</pre></div> </div></div><div class=\"sk-parallel\"><div class=\"sk-parallel-item\"><div class=\"sk-item\"><div class=\"sk-label-container\"><div class=\"sk-label  sk-toggleable\"><label>skiptransformer</label></div></div><div class=\"sk-serial\"><div class=\"sk-item\"><div class=\"sk-estimator  sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-18\" type=\"checkbox\" ><label for=\"sk-estimator-id-18\" class=\"sk-toggleable__label  sk-toggleable__label-arrow \">SkipTransformer</label><div class=\"sk-toggleable__content \"><pre>SkipTransformer()</pre></div> </div></div></div></div></div><div class=\"sk-parallel-item\"><div class=\"sk-item\"><div class=\"sk-label-container\"><div class=\"sk-label  sk-toggleable\"><label>passthrough</label></div></div><div class=\"sk-serial\"><div class=\"sk-item\"><div class=\"sk-estimator  sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-19\" type=\"checkbox\" ><label for=\"sk-estimator-id-19\" class=\"sk-toggleable__label  sk-toggleable__label-arrow \">Passthrough</label><div class=\"sk-toggleable__content \"><pre>Passthrough()</pre></div> </div></div></div></div></div></div></div><div class=\"sk-item\"><div class=\"sk-estimator  sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-20\" type=\"checkbox\" ><label for=\"sk-estimator-id-20\" class=\"sk-toggleable__label  sk-toggleable__label-arrow \">&nbsp;KNeighborsClassifier<a class=\"sk-estimator-doc-link \" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.5/modules/generated/sklearn.neighbors.KNeighborsClassifier.html\">?<span>Documentation for KNeighborsClassifier</span></a></label><div class=\"sk-toggleable__content \"><pre>KNeighborsClassifier(n_jobs=1, n_neighbors=2)</pre></div> </div></div></div></div></div></div>"
      ],
      "text/plain": [
       "Pipeline(steps=[('maxabsscaler', MaxAbsScaler()),\n",
       "                ('selectfwe', SelectFwe(alpha=0.0301998012478)),\n",
       "                ('featureunion-1',\n",
       "                 FeatureUnion(transformer_list=[('skiptransformer',\n",
       "                                                 SkipTransformer()),\n",
       "                                                ('passthrough',\n",
       "                                                 Passthrough())])),\n",
       "                ('featureunion-2',\n",
       "                 FeatureUnion(transformer_list=[('skiptransformer',\n",
       "                                                 SkipTransformer()),\n",
       "                                                ('passthrough',\n",
       "                                                 Passthrough())])),\n",
       "                ('kneighborsclassifier',\n",
       "                 KNeighborsClassifier(n_jobs=1, n_neighbors=2))])"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#access the best performing pipeline with the lowest complexity\n",
    "\n",
    "best_pipeline_lowest_complexity = sorted_pareto_front.iloc[-1]['Instance']\n",
    "best_pipeline_lowest_complexity"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Plot performance over time + Continuing a run from where it left off\n",
    "\n",
    "Plotting performance over time is a good way to assess whether or not the TPOT model has converged. If performance asymptotes over time, there may not be much more performance to be gained by running for a longer period. If the plot looks like it is still improving, it may be worth running TPOT for a longer duration. \n",
    "\n",
    "In this case, we can see that performance is near optimal and has slowed, so more time is likely unnecessary."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 1000x500 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "#get columns where roc_auc_score is not NaN\n",
    "scores_and_times = df[df['roc_auc_score'].notna()][['roc_auc_score', 'Completed Timestamp']].sort_values('Completed Timestamp', ascending=True).to_numpy()\n",
    "\n",
    "#get best score at a given time\n",
    "best_scores = np.maximum.accumulate(scores_and_times[:,0])\n",
    "times = scores_and_times[:,1]\n",
    "times = times - df['Submitted Timestamp'].min()\n",
    "\n",
    "fig, ax = plt.subplots(figsize=(10,5))\n",
    "ax.plot(times, best_scores)\n",
    "ax.set_xlabel('Time (seconds)')\n",
    "ax.set_ylabel('Best Score')\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Checkpointing\n",
    "\n",
    "There are two ways to resume TPOT. \n",
    "* If the `warm_start` parameter is set to True, subsequent calls to `fit` will continue training where it left off (The conventional scikit-learn default is to retrain from scratch on subsequent calls to fit). \n",
    "* If `periodic_checkpoint_folder` is set, TPOT will periodically save its current state to disk. If TPOT is interrupted (job canceled, PC shut off, crashes), you can resume training from where it left off. The checkpoint folder stores a data frame of all evaluated pipelines. This data frame can be loaded and inspected to help diagnose problems when debugging.\n",
    "\n",
    "\n",
    "**Note: TPOT does not clean up the checkpoint files. If the `periodic_checkpoint_folder` parameter is set, training from the last saved point will always continue, even if the input data has changed. A common issue is forgetting to change this folder between experiments and TPOT continuing training from pipelines optimized for another dataset. If you intend to start a run from scratch, you must either remove the parameter, supply an empty folder, or delete the original checkpoint folder.**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Common parameters\n",
    "\n",
    "Here is a subset of the most common parameters to customize and what they do. See the docs for `TPOTEstimator` or `TPOTEstimatorSteadyState` full documentation of all parameters. \n",
    "\n",
    "| Parameter                      | Type                  | Description                                                                 |\n",
    "|--------------------------------|-----------------------|-----------------------------------------------------------------------------|\n",
    "| scorers                        | list, scorer          | List of scorers for cross-validation; see                                    |\n",
    "| scorers_weights                | list                  | Weights applied to scorers during optimization                              |\n",
    "| classification                 | bool                  | Problem type: True for classification, False for regression                 |\n",
    "| cv                             | int, cross-validator  | Cross-validation strategy: int for folds or custom cross-validator          |\n",
    "| max_depth                      | int                   | Maximum pipeline depth                                                      |\n",
    "| other_objective_functions      | list                  | Additional objective functions; default: [average_path_length_objective]    |\n",
    "| other_objective_functions_weights | list              | Weights for additional objective functions; default: [-1]                   |\n",
    "| objective_function_names       | list                  | Names for objective functions; default: None (uses function names)          |\n",
    "| bigger_is_better               | bool                  | Optimization direction: True for maximize, False for minimize               |\n",
    "| generations                    | int                   | Number of optimization generations; default: 50                             |\n",
    "| max_time_mins                  | float                 | Maximum optimization time (minutes); default: infinite                      |\n",
    "| max_eval_time_mins             | float                 | Maximum evaluation time per individual (minutes); default: 300              |\n",
    "| n_jobs                         | int                   | Number of parallel processes; default: 1                                    |\n",
    "| memory_limit                   | str                   | Memory limit per job; default: \"4GB\"                                        |\n",
    "| verbose                        | int                   | Optimization process verbosity: 0 (none), 1 (progress), 3 (best individual), 4 (warnings), 5+ (full warnings) |\n",
    "| memory                         | str, memory object    | If supplied, pipeline will cache each transformer after calling fit with joblib.Memory. |\n",
    "| periodic_checkpoint_folder     | str                   | Folder to save the population to periodically. If None, no periodic saving will be done. If provided, training will resume from this checkpoint.|\n",
    "        \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Preventing Overfitting\n",
    "\n",
    "On small datasets, it is not impossible for TPOT to overfit the cross-validation score itself. This can lead to lower-than-expected performance on held-out datasets. TPOT will always return the model with the highest CV score as its final fitted_pipeline. However, if the highest performing model, as evaluated by cross-validation, actually was just overfit to the CV score, it may actually be worse performing compared to other models on the Pareto front.\n",
    "  * Using a secondary complexity objective and evaluating the entire pareto front may be beneficial. In some cases a lower performing pipeline with lower complexity can actually perform better on held out sets. These can either be evaluated and compared on a held out validation set, or sometimes, if very data limited, simply using a different seed of splitting the CV folds can work as well.\n",
    "    * TPOT can do this automatically. The `validation_strategy` parameter can be set to re-test the final pareto front on either a held-out validation set (percent of data set by `validation_fraction`) or a different seed for splitting the CV folds. These can be selected by setting `validation_strategy` to \"split\" or \"reshuffled\", respectively.\n",
    "  * Increasing the number of folds of cross-validation can mitigate this. \n",
    "  * Nested cross-validation can also be used to estimate the performance of the TPOT optimization algorithm itself.\n",
    "  * Removing more complex methods from the search space can reduce the chances of overfitting"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Tips and tricks for speeding up TPOT\n",
    "\n",
    "TPOT can be a computationally demanding algorithm as it fits thousands of complex machine learning pipelines on potentially large datasets. There are several strategies available for improving run time by reducing the compute needed. \n",
    "\n",
    "There are three main strategies implemented in TPOT to reduce redundant work and/or prevent wasting compute on poorly performing pipelines.\n",
    "\n",
    "1. TPOT pipelines will often have the exact same components doing the exact same computation (e.g. the first steps of the pipeline remain the same and only the parameters of the final classifier changed.) In these cases, that The first strategy is to simply cache these repeat computations so that they only happen once. More info in the next subsection.\n",
    "2. Successive Halving. This idea was first tested with TPOT by Parmentier et al. in [\"TPOT-SH: a Faster Optimization Algorithm to Solve the AutoML Problem on Large Datasets\"](https://www.researchgate.net/profile/Laurent-Parmentier-4/publication/339263193_TPOT-SH_A_Faster_Optimization_Algorithm_to_Solve_the_AutoML_Problem_on_Large_Datasets/links/5e5fd8b8a6fdccbeba1c6a56/TPOT-SH-A-Faster-Optimization-Algorithm-to-Solve-the-AutoML-Problem-on-Large-Datasets.pdf). The algorithm operates in two stages. Initially, it trains early generations using a small data subset and a large population size. Later generations then evaluate a smaller set of promising pipelines on larger, or even full, data portions. This approach rapidly identifies top-performing pipeline configurations through initial rough evaluations, followed by more comprehensive assessments. More information on this strategy in Tutorial 8.\n",
    "3. Most often, we will be evaluating pipelines using cross validation. However, we can often tell within the first few folds whether or not the pipeline is going have a reasonable change of outperforming the previous best pipelines. For example, if the best score so far is .92 AUROC and the average score of the first five folds of our current pipeline is only around .61, we can be reasonably confident that the next five folds are unlikely to this pipeline ahead of the others. We can save a significant amount of compute by not computing the rest of the folds. There are two strategies that TPOT can use to accomplish this (More information on these strategies in Tutorial 8).\n",
    "   1. Threshold Pruning: Pipelines must achieve a score above a predefined percentile threshold (based on previous pipeline scores) to proceed in each cross-validation (CV) fold.\n",
    "   2. Selection Pruning: Within each population, only the top N% of pipelines (ranked by performance in the previous CV fold) are selected to evaluate in the next fold.\"\n",
    "    \n",
    "\n",
    "## Pipeline caching in TPOT (joblib.Memory)\n",
    "\n",
    "With the memory parameter, pipelines can cache the results of each transformer after fitting them. This feature is used to avoid repeated computation by transformers within a pipeline if the parameters and input data are identical to another fitted pipeline during the optimization process. TPOT allows users to specify a custom directory path or joblib.Memory in case they want to re-use the memory cache in future TPOT runs (or a warm_start run).\n",
    "\n",
    "There are three methods for enabling memory caching in TPOT:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "from tpot import TPOTClassifier\n",
    "from tempfile import mkdtemp\n",
    "from joblib import Memory\n",
    "from shutil import rmtree\n",
    "\n",
    "# Method 1, auto mode: TPOT uses memory caching with a temporary directory and cleans it up upon shutdown\n",
    "est = TPOTClassifier(memory='auto')\n",
    "\n",
    "# Method 2, with a custom directory for memory caching\n",
    "est = TPOTClassifier(memory='/to/your/path')\n",
    "\n",
    "# Method 3, with a Memory object\n",
    "memory = Memory(location='./to/your/path', verbose=0)\n",
    "est = TPOTClassifier(memory=memory)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Note: TPOT does NOT clean up memory caches if users set a custom directory path or Memory object. We recommend that you clean up the memory caches when you don't need it anymore.**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## \n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Advanced Parallelization (HPC and multi-node training)\n",
    "\n",
    "See Tutorial 7 for more details on parallelization with Dask, including information of using multiple nodes."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# FAQ and Debugging\n",
    "\n",
    "If you are experiencing issues with TPOT, here are some common issues and how to address them.\n",
    "\n",
    "* Performance is lower than expected. What can I do?\n",
    "  * TPOT may have to be run for a longer duration, increase `max_time_mins`, `early_stop`, or `generations`.\n",
    "  * Individual pipelines may need more time to complete fitting; increase `max_eval_time_seconds.`\n",
    "  * The configuration may not include the optimal model types or hyperparameter ranges, explore other included templates, or customize your own search space (see Tutorial 2!)\n",
    "  * Check that `periodic_checkpoint_folder` is set correctly. A common issue is forgetting to change this folder between experiments and TPOT continuing training from pipelines optimized for another dataset.\n",
    "* TPOT is too slow! It is running forever and never terminating\n",
    "  * Check that at least one of the three termination conditions is set to a reasonable level. These are `max_time_mins`, `early_stop`, or `generations`. Additionally, check that `max_eval_time_seconds` gives enough time for most models to train without being overly long. (Some estimators may take an unreasonably long time to fit; this parameter is intended to prevent them from slowing everything to a halt. In my experience, SVC and SVR tend to be the culprits, so removing them from the search space may also improve run time).\n",
    "  * Set the `memory` parameter to allow TPOT to prevent repeated work when using either scikit-learn pipelines or TPOT GraphPipelines.\n",
    "  * Increase n_jobs to use more processes/CPU power. See Tutorial 7 for advanced Dask usage, including parallelizing across multiple nodes on an HPC.\n",
    "  * Use feature selection, either the build in configuration of sklearn methods (see Tutorial 2), or genetic feature selection (see Tutorials 3 and 5 for two different strategies).\n",
    "  * Use successive halving to reduce computational load (See tutorial 8).\n",
    "* Many pipelines in the evaluated_individuals data frame have crashed or turned up invalid!\n",
    "  * This is normal and is expected behavior for TPOT. In some cases, TPOT may attempt an invalid hyperparameter combination, resulting in the pipeline not working. Other times, the pipeline configuration itself may be invalid. For example, a selector may not select any features due to its hyperparameter. Another common example is `MultinomialNB` throwing an error because it expects positive values, but a prior transformation yielded a negative value. \n",
    "  * If you used custom search spaces, you can use `ConfigSpace` conditionals to prevent invalid hyperparameters (this may still occur due to how TPOT uses crossover).\n",
    "  * Setting `verbose=5` will print out the full error message for all failed pipelines. This can be useful for debugging whether or not there is something misconfigured in your pipeline, custom search space modules, or something else.\n",
    "* TPOT is crashing due to memory issues\n",
    "  * Set the `memory_limit` parameter so that n_jobs*memorylimit is less than the available RAM on your machine, plus some wiggle room. This should prevent crashing due to memory concerns.\n",
    "  * Using feature selection may also improve memory usage, as described above.\n",
    "  * Remove modules that create high RAM usage (e.g. multiple PolynomialFeatures or one with high degree).\n",
    "* Why are my TPOT runs not reproducible when random_state is set?\n",
    "  * Check that `periodic_checkpoint_folder` is set correctly. If this is set to a non-empty folder, TPOT will continue training from the checkpoint rather than start a new run from scratch. For TPOT runs to be reproducible, they have to have the same starting points.\n",
    "  * If using custom search spaces, pass in a fixed `random_state` value into the configspace of the scikit-learn modules that utilize them. TPOT does not check whether estimators do or do not take in a random state value (See Tutorial 2).\n",
    "  * If using the pre-built search spaces provided by TPOT, make sure to pass in `random_state` to `tpot.config.get_configspace` or `tpot.config.template_search_spaces.get_template_search_spaces`. This ensures all estimators that support it get a fixed random_state value. (See Tutorial 2).\n",
    "  * If using custom Node and Pipeline types, ensure all random decisions utilize the rng parameter passed into the mutation/crossover functions.\n",
    "  * If `max_eval_time_mins` is set, TPOT will terminate pipelines that exceed this time limit. If the pipeline evaluation happens to be very similar to the time limit, small random fluctuations in CPU allocation may cause a given pipeline to be evaluated in one run but not another. This slightly different result would throw off the random number generator throughout the rest of the run. Setting `max_eval_time_mins` to None or a higher value may prevent this edge case.\n",
    "  * If using `TPOTEstimatorSteadyState` with `n_jobs`>1, it is also possible that random fluctuations in CPU allocation slightly change the order in which pipelines are evaluated, which will affect the downstream results. `TPOTEstimatorSteadyState` is more reliably reproducible when `n_jobs=1` (This is not an issue for the default `TPOTEstimator`, `TPOTClassifier`, `TPOTRegressor` as they used a batched generational approach where execution order does not impact results).\n",
    "* TPOT is not using all the CPU cores I expected, given my `n_jobs` setting.\n",
    "  * The default TPOT algorithm uses a generational approach. This means the TPOT will need to evaluate `population_size` (default 50) pipelines before starting the next batch. At the end of each generation, TPOT may leave threads unused while it waits for the last few pipelines to finish evaluating. Some estimators or pipelines can be significantly slower to evaluate than others. This can be addressed in a few ways:\n",
    "    * Decrease `max_eval_time_mins` to cut long-running pipeline evaluations early.\n",
    "    * Remove estimators or hyperparameter configurations that are prone to very slow convergence (which is very often `SVC` or `SVR`).\n",
    "    * Alternatively, `TPOTEstimatorSteadyState` uses a slightly different backend for the evolutionary algorithm that does not utilize the generational approach. Instead, new pipelines are generated and evaluated as soon as the previous one finishes. With this estimator, all cores should be utilized at all times. \n",
    "    * Sometimes, setting n_jobs to a multiple of the number of threads can help minimize the chances of threads being idle while waiting for others to finish"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# More Options\n",
    "\n",
    "`tpot.TPOTClassifier` and `tpot.TPOTRegressor` have a simplified set of hyperparameters with default values set for classification and regression problems. Currently, both of these use the standard evolutionary algorithm in the `tpot.TPOTEstimator` class. If you want more control, you can look into either the `tpot.TPOTEstimator` or `tpot.TPOTEstimatorSteadyState` class.\n",
    "\n",
    "There are two evolutionary algorithms built into TPOT, which corresponds to two different estimator classes.\n",
    "\n",
    "1. The `tpot.TPOTEstimator` uses a standard evolutionary algorithm that evaluates exactly population_size individuals each generation. This is similar to the algorithm in TPOT1. The next generation does not start until the previous is completely finished evaluating. This leads to underutilized CPU time as the cores are waiting for the last individuals to finish training, but may preserve diversity in the population. \n",
    "\n",
    "2. The `tpot.TPOTEstimatorSteadyState` differs in that it will generate and evaluate the next individual as soon as an individual finishes the evaluation. The number of individuals being evaluated is determined by the n_jobs parameter. There is no longer a concept of generations. The population_size parameter now refers to the size of the list of evaluated parents. When an individual is evaluated, the selection method updates the list of parents. This allows more efficient utilization when using multiple cores.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### tpot.TPOTEstimatorSteadyState"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluations: : 119it [00:37,  3.21it/s]\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/preprocessing/_data.py:2785: UserWarning: n_quantiles (688) is greater than the total number of samples (426). n_quantiles is set to n_samples.\n",
      "  warnings.warn(\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.9816225907664725\n"
     ]
    }
   ],
   "source": [
    "import tpot\n",
    "import sklearn\n",
    "import sklearn.datasets\n",
    "\n",
    "\n",
    "graph_search_space = tpot.search_spaces.pipelines.GraphSearchPipeline(\n",
    "    root_search_space= tpot.config.get_search_space([\"KNeighborsClassifier\", \"LogisticRegression\", \"DecisionTreeClassifier\"]),\n",
    "    leaf_search_space = tpot.config.get_search_space(\"selectors\"), \n",
    "    inner_search_space = tpot.config.get_search_space([\"transformers\"]),\n",
    "    max_size = 10,\n",
    ")\n",
    "\n",
    "est = tpot.TPOTEstimatorSteadyState( \n",
    "                            search_space = graph_search_space,\n",
    "                            scorers=['roc_auc_ovr',tpot.objectives.complexity_scorer],\n",
    "                            scorers_weights=[1,-1],\n",
    "\n",
    "\n",
    "                            classification=True,\n",
    "\n",
    "                            max_eval_time_mins=15,\n",
    "                            max_time_mins=30,\n",
    "                            early_stop=10, #In TPOTEstimatorSteadyState, since there are no generations, early_stop is the number of pipelines to evaluate before stopping.\n",
    "                            n_jobs=30,\n",
    "                            verbose=2)\n",
    "\n",
    "\n",
    "scorer = sklearn.metrics.get_scorer('roc_auc_ovo')\n",
    "X, y = sklearn.datasets.load_breast_cancer(return_X_y=True)\n",
    "X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, train_size=0.75, test_size=0.25)\n",
    "est.fit(X_train, y_train)\n",
    "print(scorer(est, X_test, y_test))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "fitted_pipeline = est.fitted_pipeline_ # access best pipeline directly\n",
    "fitted_pipeline.plot()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>roc_auc_score</th>\n",
       "      <th>complexity_scorer</th>\n",
       "      <th>Parents</th>\n",
       "      <th>Variation_Function</th>\n",
       "      <th>Individual</th>\n",
       "      <th>Submitted Timestamp</th>\n",
       "      <th>Completed Timestamp</th>\n",
       "      <th>Eval Error</th>\n",
       "      <th>Pareto_Front</th>\n",
       "      <th>Instance</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>0.841954</td>\n",
       "      <td>95.0</td>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>&lt;tpot.search_spaces.pipelines.graph.GraphPipel...</td>\n",
       "      <td>1.740179e+09</td>\n",
       "      <td>1.740179e+09</td>\n",
       "      <td>None</td>\n",
       "      <td>NaN</td>\n",
       "      <td>[('DecisionTreeClassifier_1', 'SelectPercentil...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>0.967781</td>\n",
       "      <td>89.0</td>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>&lt;tpot.search_spaces.pipelines.graph.GraphPipel...</td>\n",
       "      <td>1.740179e+09</td>\n",
       "      <td>1.740179e+09</td>\n",
       "      <td>None</td>\n",
       "      <td>NaN</td>\n",
       "      <td>[('DecisionTreeClassifier_1', 'SelectFwe_1'), ...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>0.972412</td>\n",
       "      <td>22.0</td>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>&lt;tpot.search_spaces.pipelines.graph.GraphPipel...</td>\n",
       "      <td>1.740179e+09</td>\n",
       "      <td>1.740179e+09</td>\n",
       "      <td>None</td>\n",
       "      <td>NaN</td>\n",
       "      <td>[('KNeighborsClassifier_1', 'ColumnOneHotEncod...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>0.975926</td>\n",
       "      <td>54.0</td>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>&lt;tpot.search_spaces.pipelines.graph.GraphPipel...</td>\n",
       "      <td>1.740179e+09</td>\n",
       "      <td>1.740179e+09</td>\n",
       "      <td>None</td>\n",
       "      <td>NaN</td>\n",
       "      <td>[('KNeighborsClassifier_1', 'SelectFwe_1')]</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>0.964352</td>\n",
       "      <td>84.0</td>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>&lt;tpot.search_spaces.pipelines.graph.GraphPipel...</td>\n",
       "      <td>1.740179e+09</td>\n",
       "      <td>1.740179e+09</td>\n",
       "      <td>None</td>\n",
       "      <td>NaN</td>\n",
       "      <td>[('DecisionTreeClassifier_1', 'ZeroCount_1'), ...</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   roc_auc_score  complexity_scorer Parents Variation_Function  \\\n",
       "0       0.841954               95.0     NaN                NaN   \n",
       "1       0.967781               89.0     NaN                NaN   \n",
       "2       0.972412               22.0     NaN                NaN   \n",
       "3       0.975926               54.0     NaN                NaN   \n",
       "4       0.964352               84.0     NaN                NaN   \n",
       "\n",
       "                                          Individual  Submitted Timestamp  \\\n",
       "0  <tpot.search_spaces.pipelines.graph.GraphPipel...         1.740179e+09   \n",
       "1  <tpot.search_spaces.pipelines.graph.GraphPipel...         1.740179e+09   \n",
       "2  <tpot.search_spaces.pipelines.graph.GraphPipel...         1.740179e+09   \n",
       "3  <tpot.search_spaces.pipelines.graph.GraphPipel...         1.740179e+09   \n",
       "4  <tpot.search_spaces.pipelines.graph.GraphPipel...         1.740179e+09   \n",
       "\n",
       "   Completed Timestamp Eval Error  Pareto_Front  \\\n",
       "0         1.740179e+09       None           NaN   \n",
       "1         1.740179e+09       None           NaN   \n",
       "2         1.740179e+09       None           NaN   \n",
       "3         1.740179e+09       None           NaN   \n",
       "4         1.740179e+09       None           NaN   \n",
       "\n",
       "                                            Instance  \n",
       "0  [('DecisionTreeClassifier_1', 'SelectPercentil...  \n",
       "1  [('DecisionTreeClassifier_1', 'SelectFwe_1'), ...  \n",
       "2  [('KNeighborsClassifier_1', 'ColumnOneHotEncod...  \n",
       "3        [('KNeighborsClassifier_1', 'SelectFwe_1')]  \n",
       "4  [('DecisionTreeClassifier_1', 'ZeroCount_1'), ...  "
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#view the summary of all evaluated individuals as a pandas dataframe\n",
    "est.evaluated_individuals.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### tpot.TPOTEstimator"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Generation: : 5it [10:06, 121.38s/it]\n",
      "/opt/anaconda3/envs/tpotenv/lib/python3.10/site-packages/sklearn/linear_model/_sag.py:349: ConvergenceWarning: The max_iter was reached which means the coef_ did not converge\n",
      "  warnings.warn(\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.996046608406159\n"
     ]
    }
   ],
   "source": [
    "import tpot\n",
    "import sklearn\n",
    "import sklearn.datasets\n",
    "\n",
    "est = tpot.TPOTEstimator(  \n",
    "                            search_space = graph_search_space,\n",
    "                            max_time_mins=10,\n",
    "                            scorers=['roc_auc_ovr'], #scorers can be a list of strings or a list of scorers. These get evaluated during cross validation. \n",
    "                            scorers_weights=[1],\n",
    "                            classification=True,\n",
    "                            n_jobs=1, \n",
    "                            early_stop=5, #how many generations with no improvement to stop after\n",
    "                            \n",
    "                            #List of other objective functions. All objective functions take in an untrained GraphPipeline and return a score or a list of scores\n",
    "                            other_objective_functions= [ ],\n",
    "                            \n",
    "                            #List of weights for the other objective functions. Must be the same length as other_objective_functions. By default, bigger is better is set to True. \n",
    "                            other_objective_functions_weights=[],\n",
    "                            verbose=2)\n",
    "\n",
    "scorer = sklearn.metrics.get_scorer('roc_auc_ovo')\n",
    "X, y = sklearn.datasets.load_breast_cancer(return_X_y=True)\n",
    "X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, train_size=0.75, test_size=0.25)\n",
    "est.fit(X_train, y_train)\n",
    "print(scorer(est, X_test, y_test))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Regression Example"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Generation: : 24it [18:15, 45.63s/it]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "-2968.0005982574958\n"
     ]
    }
   ],
   "source": [
    "import tpot\n",
    "import sklearn\n",
    "import sklearn.metrics\n",
    "import sklearn.datasets\n",
    "\n",
    "scorer = sklearn.metrics.get_scorer('neg_mean_squared_error')\n",
    "X, y = sklearn.datasets.load_diabetes(return_X_y=True)\n",
    "X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, train_size=0.75, test_size=0.25)\n",
    "\n",
    "est = tpot.tpot_estimator.templates.TPOTRegressor(n_jobs=4, max_time_mins=30, verbose=2, cv=5, early_stop=5)\n",
    "est.fit(X_train, y_train)\n",
    "\n",
    "print(scorer(est, X_test, y_test))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "tpotenv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.16"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
